HLSL Pixel Shader 5.0 NdotL lighting sample - c++

I am going through a tutorial for Pixel Shader 5.0 using directX. I was doing fine until I got to the fourth lesson, which has me create a PixelShader.hlsl file with the following code:
cbuffer ConstantBuffer : register( b0 )
{
matrix World;
matrix View;
matrix Proj;
float4 vLightDir[2];
float4 vLightColor[2];
float4 vOutputColor;
}
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float3 Norm : TEXCOORD0;
};
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 main( PS_INPUT input) : SV_Target
{
float4 finalColor = 0;
//do NdotL lighting for 2 lights
for(int i=0; i<2; i=""
{
finalColor += "saturate"( dot=""( (float3)vLightDir=""[i=""],input.Norm="") * vLightColor=""[i=""
}
finalColor.a = "1"
return="" finalColor="";
}
The for loop at the end of the file has me confused. Int i is being initialized to a constant string, and there is no closing parenthesis.
Similarly, the code within the for loop looks incorrect as well. The float4 struct appears to have a string "saturate" being appended to it, which is also a function? A function without proper closing braces and no semicolon no less.
It looks like there are some typos in the sample code, and I'm struggling to fix them. Any thoughts as to what should be written here?
When I try to compile the code in VS2012, I get the following error: error X3017: cannot implicitly convert from 'const string' to 'int'
I tried fixing the for loop code by changing it to
for(int i=0, i<2, i++)
and I get a different error for the code within the for loop: error X3022: scalar, vector, or matrix expected
Thanks in advance for your help

I finally got a response from the site author. Turns out an XML converter auto-formatted the code sample and messed some things up. Here is the proper code:
cbuffer ConstantBuffer : register( b0 )
{
matrix World;
matrix View;
matrix Proj;
float4 vLightDir[2];
float4 vLightColor[2];
float4 vOutputColor;
}
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float3 Norm : TEXCOORD0;
};
//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 main( PS_INPUT input) : SV_Target
{
float4 finalColor = 0;
//do NdotL lighting for 2 lights
for(int i=0; i<2; i++)
{
finalColor += saturate( dot( (float3)vLightDir[i],input.Norm) * vLightColor[i] );
}
finalColor.a = 1;
return finalColor;
}

Related

Geometry Shader must have a max vertex count DirectX 11

I am trying to add a Geometry Shader to my DirectX 11 project in C++
There are no examples of this anywhere I look. There are millions of tutorials on OpenGL but nothing on geometry shaders in DirectX
I just wrote a basic shader below, but I get the following error when trying to build it
error X3514: 'LightGeometryShader' must have a max vertex count
Can anyone please advise on what this shader is missing to be able to compile?
////////////////////////////////////////////////////////////////////////////////
// Filename: light.gs
////////////////////////////////////////////////////////////////////////////////
//////////////
// TYPEDEFS //
//////////////
struct GeometryInputType
{
float4 position : POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
};
struct PixelInputType
{
float4 position : SV_POSITION;
float2 tex : TEXCOORD0;
float3 normal : NORMAL;
};
////////////////////////////////////////////////////////////////////////////////
// Geometry Shader
////////////////////////////////////////////////////////////////////////////////
PixelInputType LightGeometryShader(GeometryInputType input)
{
PixelInputType output;
output = input;
return output;
}
GeometryShader is not necessarily a 1:1 function, which is why you have to provide a max vertex count. See Microsoft Docs.
[maxvertexcount(3)]
void LightGeometryShader( triangle GeometryInputType input[3],
inout TriangleStream<PixelInputType> outStream )
{
PixelInputType output;
for( int v = 0; v < 3; v++ )
{
output.position = input[v].position;
output.tex = input[v].tex;
output.normal = input[v].normal;
outStream.Append( output );
}
}
Geometry Shader was introduced with Direct3D 10, so the bulk of the deved samples were in the legacy DirectX SDK at the time. You can find the latest copy of these samples buildable without the legacy DirectX SDK on GitHub.

How to use pixel shader to render material from a Wavefront Obj file?

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..

Adding geometry shader causes "shader linkage error"

I'm adding a geometry shader (a very simple one) to my DirectX 11 program. I've already got vertex and pixel shader written, and they work just as expected - no errors, no warnings. The shaders are simple, too. The vertex shader is:
cbuffer PerApplication : register(b0)
{
matrix projectionMatrix;
}
cbuffer PerFrame : register(b1)
{
matrix viewMatrix;
}
cbuffer PerObject : register(b2)
{
matrix worldMatrix;
}
struct VertexShaderInput
{
float4 position : POSITION;
float4 color: COLOR;
};
struct VertexShaderOutput
{
float4 color : COLOR;
float4 position : SV_POSITION;
};
//entry point
VertexShaderOutput SimpleVertexShader(VertexShaderInput IN)
{
VertexShaderOutput OUT;
matrix mvp = mul(projectionMatrix, mul(viewMatrix, worldMatrix));
OUT.color = IN.color;
OUT.position = mul(mvp, IN.position);
return OUT;
}
The pixel shader is:
struct PixelShaderInput
{
float4 color : COLOR;
};
float4 SimplePixelShader(PixelShaderInput IN) : SV_TARGET
{
return IN.color;
}
Well, as I've said, that's working pretty well. Then I'm adding a geometry shader, which doesn't actually do anything, it just takes a triangle and returns the same triangle. The geometry shader is:
struct VertexInput
{
float4 color : COLOR;
float4 position : POSITIONT;
};
struct VertexOutput
{
float4 color : COLOR;
float4 position : SV_Position;
};
[maxvertexcount(3)]
void SimpleGeometryShader(triangle VertexInput input[3], inout TriangleStream<VertexOutput> stream)
{
VertexOutput v1 = { input[0].color, input[0].position };
stream.Append(v1);
VertexOutput v2 = { input[1].color, input[1].position };
stream.Append(v2);
VertexOutput v3 = { input[2].color, input[2].position };
stream.Append(v3);
stream.RestartStrip();
}
Doing this also requires to change the vertex shader, which now returns
struct VertexShaderOutput
{
float4 color : COLOR;
float4 position : POSITIONT; //I'm not returning SV_Position in vertex shader anymore.
};
And the program itself works, and it works as expected, I see what I expect to see. But there are now two D3D11 errors:
D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Vertex Shader - Geometry Shader linkage error: Signatures between stages are incompatible. The input stage requires Semantic/Index (POSITIONT,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]
D3D11 ERROR: ID3D11DeviceContext::DrawIndexed: Geometry Shader - Pixel Shader linkage error: Signatures between stages are incompatible. The input stage requires Semantic/Index (TEXCOORD,0) as input, but it is not provided by the output stage. [ EXECUTION ERROR #342: DEVICE_SHADER_LINKAGE_SEMANTICNAME_NOT_FOUND]
Both are pretty strange. The vertex shader clearly returns a POSITIONT, and COLOR and POSITIONT are in the same order. What's my mistake?
The reason of the problem was that beside of rendering the scene using the shaders that I provided I was also drawing text using some library. Obviously, there were some vertex and pixel shaders involved in it with their own input and output signatures. Setting geometry shader to null solved my problem.

Frame rotation with HLSL

We have some media processing application, and we need ability for rotate frames.
We use GPU.
We have Vertex Shader in HLSL with follow code:
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
uint TexIdx : TEXINDEX;
};
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
uint TexIdx : TEXINDEX;
};
VS_OUTPUT VS(VS_INPUT input)
{
VS_OUTPUT output;
float2 pos_rotate = input.Pos.xy;
float rads = radians(45);
float cFlare = cos(rads);
float sFlare = sin(rads);
output.Pos = input.Pos;
output.Pos.xy = mul(pos_rotate, float2x2(cFlare, -sFlare, sFlare, cFlare));
output.Tex = input.Tex;
output.TexIdx = input.TexIdx;
return output;
};
So, as I understand, sin and cos get angle in radians.
radians get angle in degrees.
But as we see, the frame came rotated to 90° and not 45°.
ОК, the problem in C++ code, in other call stack the frame rotates as expected. But it difficult now to provide the C++ code... I think, I will put the frame just in right call stack.

DirectX HLSL - Constant Buffer empty in Pixel Shader but not Vertex Shader

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) {