I am in the process of implementing lighting in my DirectX 11 project. The problem I have is that when I try to access a cbuffer value from the Pixel Shader function it's just returning float3(0, 0, 0) meanwhile when I access the same value in the Vertex Shader function it returns the correct value. Here is the shader:
/*********************************************\
VERTEX SHADER
\*********************************************/
//Constant buffers
cbuffer Object : register(cb0) {
float4x4 WorldMatrix;
};
cbuffer Camera : register(cb1) {
float4x4 ViewMatrix;
float4x4 ProjectionMatrix;
};
cbuffer LightBuffer : register(cb2) {
float3 AmbientColor;
}
//IO Structs
struct VS_INPUT {
float3 Position : POSITION;
float2 UV : TEXCOORD;
float3 Normal : NORMAL;
};
struct VS_OUTPUT {
float4 Position : SV_POSITION;
float2 UV : TEXCOORD;
float3 Normal : NORMAL;
};
VS_OUTPUT VS(VS_INPUT input){
VS_OUTPUT output;
float4 Position;
//Multiply position with AmbientColor (should be 1, 1, 1), position unchanged
Position = mul(ViewMatrix, float4(input.Position * AmbientColor, 1));
Position = mul(ProjectionMatrix, Position);
Position = mul(WorldMatrix, Position);
output.Position = Position;
output.UV = input.UV;
output.Normal = mul(WorldMatrix, input.Normal);
return output;
}
/*********************************************\
PIXEL SHADER
\*********************************************/
SamplerState TextureState;
Texture2D<float4> Texture;
float4 PS(VS_OUTPUT input) : SV_TARGET {
float4 MaterialColor = Texture.Sample(TextureState, input.UV);
//Multiply color with AmbientColor (should be 1, 1, 1), returns black
float3 FinalColor = MaterialColor.xyz * AmbientColor;
return float4(FinalColor, MaterialColor.a);
}
Here's is the value I'm sending (c++):
_LightsUniform.AmbientColor = XMFLOAT3(1, 1, 1);
DeviceContext->UpdateSubresource(_LightBuffer, 0, NULL, &_LightsUniform, 0, 0);
DeviceContext->VSSetConstantBuffers(2, 1, &_LightBuffer);
DeviceContext->PSSetConstantBuffers(2, 1, &_LightBuffer);
Here is the result:
http://i.gyazo.com/357f1ed3ea33e6569ad2346b368cd975.png
And result without multiplying color: http://gyazo.com/b60b385daa94d3373e9552a523928e3f
I can't see what is wrong. Anybody else had the same issue?
I found the problem. Turns out that the registers for my cbuffer(s) were wrong, I used cb# where b# should be used. (I misunderstood what was written here: https://msdn.microsoft.com/en-us/library/windows/desktop/hh447212(v=vs.85).aspx)
Wrong code:
cbuffer LightBuffer : register(cb2) {
Changed to:
cbuffer LightBuffer : register(b2) {
Related
Some 3d meshes that get exported to Wavefront.obj format usually come with a .mtl file that has additional data to the texture it uses and its materials, when exported from Blender they always come with Ambient, Diffuse, Specular, and Emissive RGB data as part of its material, but I'm not sure how I can use this data in the pixel shader and get the right color output.
I would appreciate it if anyone can explain to me how to use these materials and any code sample would be very welcome.
Traditional materials and lighting models use "Ambient", "Diffuse", "Specular", and "Emissive" colors which is why you find those in Wavefront OBJ files. These can often be replaced or used in multiplicative conjunction with texture colors.
The (now defunct) XNA Game Studio product did a good job of providing simple 'classic' shaders in the BasicEffect "Stock Shaders". I use them in the DirectX Tool Kit for DX11 and DX12.
Take a look at BasicEffect.fx for a traditional material pixel shader. If you are looking mostly for pixel-shader handling, that's "per-pixel lighting" as opposed to "vertex lighting" which was more common back when we had less powerful GPUs.
Here's a 'inlined' version so you can follow it all in one place:
struct VSInputNmTx
{
float4 Position : SV_Position;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
Texture2D<float4> Texture : register(t0);
sampler Sampler : register(s0);
cbuffer Parameters : register(b0)
{
float4 DiffuseColor : packoffset(c0);
float3 EmissiveColor : packoffset(c1);
float3 SpecularColor : packoffset(c2);
float SpecularPower : packoffset(c2.w);
float3 LightDirection[3] : packoffset(c3);
float3 LightDiffuseColor[3] : packoffset(c6);
float3 LightSpecularColor[3] : packoffset(c9);
float3 EyePosition : packoffset(c12);
float3 FogColor : packoffset(c13);
float4 FogVector : packoffset(c14);
float4x4 World : packoffset(c15);
float3x3 WorldInverseTranspose : packoffset(c19);
float4x4 WorldViewProj : packoffset(c22);
};
struct VSOutputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
float4 PositionPS : SV_Position;
};
// Vertex shader: pixel lighting + texture.
VSOutputPixelLighting VSBasicPixelLightingTx(VSInputNmTx vin)
{
VSOutputPixelLighting vout;
vout.PositionPS = mul(vin.Position, WorldViewProj);
vout.PositionWS.xyz = mul(vin.Position, World).xyz;
// ComputeFogFactor
vout.PositionWS.w = saturate(dot(vin.Position, FogVector));
vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose));
vout.Diffuse = float4(1, 1, 1, DiffuseColor.a);
vut.TexCoord = vin.TexCoord;
return vout;
}
struct PSInputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
};
// Pixel shader: pixel lighting + texture.
float4 PSBasicPixelLightingTx(PSInputPixelLightingTx pin) : SV_Target0
{
float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse;
float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz);
float3 worldNormal = normalize(pin.NormalWS);
ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3);
color.rgb *= lightResult.Diffuse;
// AddSpecular
color.rgb += lightResult.Specular * color.a;
// ApplyFog (we passed fogfactor in via PositionWS.w)
color.rgb = lerp(color.rgb, FogColor * color.a, pin.PositionWS.w);
return color;
}
Here is the helper function ComputeLights which implements a Blinn-Phong reflection model for the specular highlight.
struct ColorPair
{
float3 Diffuse;
float3 Specular;
};
ColorPair ComputeLights(float3 eyeVector, float3 worldNormal, uniform int numLights)
{
float3x3 lightDirections = 0;
float3x3 lightDiffuse = 0;
float3x3 lightSpecular = 0;
float3x3 halfVectors = 0;
[unroll]
for (int i = 0; i < numLights; i++)
{
lightDirections[i] = LightDirection[i];
lightDiffuse[i] = LightDiffuseColor[i];
lightSpecular[i] = LightSpecularColor[i];
halfVectors[i] = normalize(eyeVector - lightDirections[i]);
}
float3 dotL = mul(-lightDirections, worldNormal);
float3 dotH = mul(halfVectors, worldNormal);
float3 zeroL = step(0, dotL);
float3 diffuse = zeroL * dotL;
float3 specular = pow(max(dotH, 0) * zeroL, SpecularPower) * dotL;
ColorPair result;
result.Diffuse = mul(diffuse, lightDiffuse) * DiffuseColor.rgb + EmissiveColor;
result.Specular = mul(specular, lightSpecular) * SpecularColor;
return result;
}
These BasicEffect shaders don't make use of ambient color, but you could modify them to do so if you wanted. All ambient color does is provide a 'minimum color value' that's independent of dynamic lights.
Note that there also some unofficial Physically-Based Rendering (PBR) materials extension in some Wavefront OBJ files. See Extending Wavefront MTL for Physically-Based. More modern geometry formats like glTF assume PBR materials properties which is things like an albedo texture, normal texture, roughness/metalness texture, etc..
I am working on a C++ 3D game and I am having trouble with how the mesh looks when I am loading it from a glTF file on the screen.
This is the 3D model I am trying to load.
I am using DirectX 11.
As you can see the mesh is flipped on the X-axis and I can't figure what to do to flip to look normal.
This is how I want it to look.
struct VS_INPUT
{
float4 position: POSITION0;
float2 texcoord: TEXCOORD0;
float3 normal: NORMAL0;
};
struct VS_OUTPUT
{
float4 position: SV_POSITION;
float2 texcoord: TEXCOORD0;
float3 normal: NORMAL0;
float3 direction_to_camera: TEXCOORD1;
};
cbuffer constant: register(b0)
{
row_major float4x4 m_world;
row_major float4x4 m_view;
row_major float4x4 m_proj;
float4 m_light_direction;
float4 m_camera_position;
float4 m_light_position;
float m_light_radius;
};
VS_OUTPUT vsmain(VS_INPUT input)
{
VS_OUTPUT output = (VS_OUTPUT)0;
// WORLD SPACE
output.position = mul(input.position, m_world);
output.direction_to_camera = normalize(output.position - m_camera_position.xyz);
// VIEW SPACE
output.position = mul(output.position, m_view);
// SCREEN SPACE
output.position = mul(output.position, m_proj);
output.texcoord = input.texcoord;
output.normal = normalize(mul(input.normal, m_world));
return output;
}
This is the vertex shader I am using.
output.position.x = -output.position.x; in vertex shader
or when loading your mesh and parsing vertices invert x coordinate of vertex.
v.x = -v.x;
or scaling -1 on x axis should do the trick.
SetScale( -1.0f, 1.0f, 1.0f );
I'm working on a graphics program in DirectX11 which takes a heightmap, manipulates a plane based on the heightmap, calculates the normals, and lights the scene with a directional light. I'm trying to get shadow mapping to work with the directional light, but whenever I do the depth test in the pixel shader it always returns 1, instead of the expected value.
Here's my main pixel shader
Texture2D t0 : register(t0);
Texture2D t1 : register(t1);
SamplerState s0 : register(s0);
SamplerState s1 : register(s1);
cbuffer LightBuffer : register(b0)
{
float4 diffuseColour;
float3 lightDirection;
float padding;
}
struct InputType
{
float4 position : SV_POSITION;
float4 tex: TEXCOORD0;
float3 normal : normal;
float4 lightViewPos : TEXCOORD1;
};
float4 calculateLighting(float3 lightDirection, float3 normal, float4 diffuse)
{
float intensity = saturate(dot(normal, lightDirection));
float4 colour = saturate(diffuse * intensity);
return colour;
}
float4 main(InputType input) : SV_TARGET
{
float depthValue;
float lightDepthValue;
float shadowMapBias = 0.005f;
float4 colour = float4(0.f, 0.f, 0.f, 1);
float4 textureColour = t0.Sample(s0, input.tex.xy);
float2 pTexCoord = input.lightViewPos.xy / input.lightViewPos.w;
pTexCoord *= float2(0.5, -0.5);
pTexCoord += float2(0.5, 0.5);
if (pTexCoord.x < 0.0f || pTexCoord.x > 1.0f ||
pTexCoord.y < 0.0f || pTexCoord.y > 1.0f)
{
return textureColour;
}
depthValue = t1.Sample(s1, pTexCoord).r;
lightDepthValue = input.lightViewPos.z / input.lightViewPos.w;
lightDepthValue -= shadowMapBias;
if (lightDepthValue < depthValue)
{
//colour = float4(1, 1, 1, 1);
colour = calculateLighting(-lightDirection, input.normal, diffuseColour);
}
// This is a test to see what the depth value outputs as
if (depthValue == 0)
return float4(1, 0, 1, 1);
else if (depthValue == 1)
return float4(1, 1, 1, 1); // Always returns this
else
return float4(0, 1, 0, 1);
//return float4(depthValue, depthValue, depthValue, 1.0f);
// return saturate(colour * textureColour);
}
My depth vertex shader:
Texture2D t0 : register(t0);
SamplerState s0 : register(s0);
cbuffer MatrixBuffer : register(b0)
{
matrix worldMatrix;
matrix viewMatrix;
matrix projectionMatrix;
}
struct InputType
{
float4 position : position;
float2 tex : TEXCOORD0;
};
struct OutputType
{
float4 position : SV_POSITION;
float4 depthPosition : TEXCOORD0;
};
OutputType main(InputType input)
{
OutputType output;
output.position = mul(input.position, worldMatrix);
if (t0.SampleLevel(s0, input.tex, 0).r > 0.05f)
output.position.y += (t0.SampleLevel(s0, input.tex, 0).r * 50.0f);
output.position = mul(output.position, viewMatrix);
output.position = mul(output.position, projectionMatrix);
output.depthPosition = output.position;
return output;
}
And my depth pixel shader:
struct InputType
{
float4 position : SV_POSITION;
float4 depthPosition : TEXCOORD0;
};
float4 main(InputType input) : SV_Target
{
float depthValue;
depthValue = input.depthPosition.z / input.depthPosition.w;
return float4(depthValue, depthValue, depthValue, 1.0f);
}
I know the depth data is being written to the render texture correctly as I have a preview of it on the screen current view + normals shown to demonstrate topography instead of a flat white plane.
So I have added MRT to my program.
The Problem: My two textures (normalMap and positionMap) are empty (only filled with the clearcolor)..
I use them like this:
Inside of my header:
ID3D10RenderTargetView** m_ppBuffer;
ID3D10Texture** m_ppTextures;
ID3D10ShaderResourceView** m_ppshaderView;
Each Frame:
this->m_pDevice->OMSetRenderTargets(this->m_TextureCount, this->m_ppBuffer, this->m_pDepthBuffer);
for (int32 i = 0; i < this->m_TextureCount; i++)
this->m_pDevice->ClearRenderTargetView(this->m_pBuffer[i], a_Color.colors);
....
....
this->m_pEffect->GetVariableByName("DiffuseMap")->AsShaderResource()->SetResource(m_ppshaderView[0]);
this->m_pEffect->GetVariableByName("NormalMap")->AsShaderResource()->SetResource(m_ppshaderView[1]);
this->m_pEffect->GetVariableByName("PositionMap")->AsShaderResource()->SetResource(m_ppshaderView[2]);
this->m_pDevice->IASetInputLayout(this->m_pInputLayout);
this->m_pTechnique->GetPassByIndex(0)->Apply(0);
this->m_pDevice->IASetPrimitiveTopology(primitiveTopology);
this->m_pDevice->Draw(a_Vertices, 0);
My Shader which I use to draw onto the rendertargets:
float4x4 Scale;
float4x4 Rotation;
float4x4 Translation;
float4x4 View;
float4x4 Projection;
Texture2D Diffuse;
SamplerState TextureSampler
{
Filter = MIN_MAG_MIP_Linear;
AddressU = WRAP;
AddressV = WRAP;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float2 UV : TEXCOORD0;
float3 Color : COLOR0;
float3 Normal : NORMAL0;
};
struct VertexShaderOutput
{
float4 PositionOut : SV_POSITION;
float4 Position : POSITION0;
float2 UV : TEXCOORD0;
float3 Color : COLOR0;
float3 Normal : NORMAL0;
};
struct PixelShaderOutput
{
float4 colorMap: SV_TARGET0;
float4 normalMap: SV_TARGET1;
float4 positionMap: SV_TARGET2;
};
VertexShaderOutput VS_MAIN(VertexShaderInput input)
{
VertexShaderOutput Output = (VertexShaderOutput)0;
float4 pos = float4(input.Position.xyz, 1);
float4x4 Model = mul(mul(Scale,Rotation),Translation);
float4x4 MVP = mul(mul(Model,View),Projection);
Output.Position = mul(pos,MVP);
Output.PositionOut = mul(pos,MVP);
Output.UV = input.UV;
Output.Color = input.Color;
Output.Normal = input.Normal;
return Output;
}
PixelShaderOutput PS_MAIN(VertexShaderOutput input)
{
PixelShaderOutput output;
float4 blend = Diffuse.Sample(TextureSampler, input.UV);
output.colorMap = blend;
output.normalMap = float4(input.Normal,1);
output.positionMap = float4(input.Position.xyz,1);
return output;
}
RasterizerState Culling
{
CullMode = 1;
};
technique10 Main
{
pass p0
{
SetRasterizerState(Culling);
SetVertexShader(CompileShader(vs_4_0, VS_MAIN()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PS_MAIN()));
}
};
After that, I render my Rendertargets using one Quad and this shader.
TextureID is a value I defined in code so I can switch between the rendertarget textures during runtime using my arrows.
float4x4 Scale;
float4x4 Rotation;
float4x4 Translation;
float4x4 View;
float4x4 Projection;
float textureID;
Texture2D DiffuseMap;
Texture2D NormalMap;
Texture2D PositionMap;
SamplerState TextureSampler
{
Filter = MIN_MAG_MIP_Linear;
AddressU = WRAP;
AddressV = WRAP;
};
struct VertexShaderInput
{
float3 Position : POSITION0;
float2 UV : TEXCOORD0;
float3 Color : COLOR0;
float3 Normal : NORMAL;
};
struct VertexShaderOutput
{
float4 Position : SV_POSITION;
float2 UV : TEXCOORD0;
float3 Color : COLOR0;
float3 Normal : NORMAL;
};
struct PixelShaderOutput
{
float4 color: SV_TARGET0;
};
VertexShaderOutput VS_MAIN(VertexShaderInput input)
{
VertexShaderOutput Output = (VertexShaderOutput)0;
float4 pos = float4(input.Position.xyz, 1);
float4x4 Model = mul(mul(Scale,Rotation),Translation);
float4x4 MVP = mul(mul(Model,View),Projection);
Output.Position = mul(pos,MVP);
Output.UV = input.UV;
Output.Color = input.Color;
Output.Normal = input.Normal;
return Output;
}
PixelShaderOutput PS_MAIN(VertexShaderOutput input)
{
PixelShaderOutput output;
float4 blend = DiffuseMap.Sample(TextureSampler, input.UV);
float4 norm = NormalMap.Sample(TextureSampler, input.UV);
float4 pos = PositionMap.Sample(TextureSampler, input.UV);
blend.a = 1;
if(textureID == 0.0)
output.color = blend;
else if(textureID == 1.0)
output.color = norm;
else
output.color = pos;
return output;
}
RasterizerState Culling
{
CullMode = 1;
};
technique10 Main
{
pass p0
{
SetRasterizerState(Culling);
SetVertexShader(CompileShader(vs_4_0, VS_MAIN()));
SetGeometryShader(NULL);
SetPixelShader(CompileShader(ps_4_0, PS_MAIN()));
}
};
I used the graphic debugger and that gave me the reason why it did not work!
I forgot to set the BlendState for my other Rendertarget's. Setting BlendEnable to true fixed it finally.
I have the following vertex and pixel shaders:
struct VS_INPUT
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Color : TEXCOORD1;
};
struct VS_OUTPUT
{
float4 Position : POSITION0;
float4 Color : COLOR0;
float2 TexCoord : TEXCOORD0;
};
float4x4 projview_matrix;
VS_OUTPUT vs_main(VS_INPUT Input)
{
VS_OUTPUT Output;
Output.Position = mul(Input.Position, projview_matrix);
Output.Color = Input.Color;
Output.TexCoord = Input.TexCoord;
return Output;
}
px
texture tex;
sampler2D s = sampler_state {
texture = <tex>;
};
float4 ps_main(VS_OUTPUT Input) : COLOR0
{
float4 pixel = tex2D(s, Input.TexCoord.xy);
return pixel;
}
This is for a 2d game. The vertices of the quads contain tinting colors that I want to use to tint the bitmap. How can I obtain the color of the current vertex so I can multiply it in the pixel shader by the current pixel color?
Thanks
In your pixel shader, do:
float4 pixel = tex2D(s, Input.TexCoord.xy) * Input.Color;
The Input.Color value will be linearly interpreted across your plane for you, just like Input.TexCoord is. To blend two color vectors together, you simply multiply them together. It may also be advisable to do:
float4 pixel = tex2D(s, Input.TexCoord.xy) * Input.Color;
pixel = saturate(pixel);
The saturate() function will clip each RGB value in your color in the range of 0.0 to 1.0, which may avoid any possible display artifacts.