I'm having an issue where my shader code only executes properly if I set the pixel shader constant buffer at registerb1
to the struct below. I've been looking all over for answers and I've probably spent a collective 8 hours trying to solve this. It seems that the variable time
from ConstantBuffer
is getting put in either of the variables of CameraBuffer
. I thought __declspec(align(16))
fixed the padding issue. I believe I've also padded things correctly anyway. I've placed comments throughout my code.
// A buffer for lighting
__declspec(align(16))
struct CameraBuffer {
Vec3f position{};
Vec3f direction{};
Vec2f pad{};
};
// The buffer required for proper results, even though it shouldn't be required.
__declspec(align(16))
struct ConstantBuffer {
float time;
float period;
float amplitude;
};
// Method for drawing a sphere
void DXGLApp::drawMaterialSphere(Vec3f position, dxgl::SP_DXGLMaterial material) {
// transform data
dxgl::buffer::TransformBuffer tbuff1{};
tbuff1.world.setIdentity();
tbuff1.world.setTranslation(position);
tbuff1.view = m_camera->view();
tbuff1.proj = m_camera->proj();
m_cbLandscape->update(&tbuff1);
// setting vertex data
render()->stageInput()->setInputLayout(m_layout);
render()->stageInput()->setVertexBuffers(1, &m_mesh1->getVertexBuffer());
render()->stageInput()->setIndexBuffer(m_mesh1->getIndexBuffer());
// setting shaders
render()->stageShader()->VS_setShader(m_vs);
render()->stageShader()->PS_setShader(m_ps);
// setting cbuffers
render()->stageShader()->VS_setCBuffer(0, 1, m_cbLandscape->get());
render()->stageShader()->PS_setCBuffer(0, 1, m_cbCamera->get());
// This cbuffer, 'm_cbPostProcess' is not used in the currently bound shader,
// yet the results produced without it are incorrect.
render()->stageShader()->PS_setCBuffer(1, 1, m_cbPostProcess->get());
// sets texture slots
render()->stageShader()->PS_setMaterial(0, 6, material);
// draw
render()->stageInput()->drawIndexedTriangleList(m_mesh1->getIndices().size(), 0, 0);
// unbind
//render()->stageShader()->VS_setShader(0);
//render()->stageShader()->PS_setShader(0);
//render()->stageShader()->VS_setCBuffer(0, 0, 0);
//render()->stageShader()->PS_setCBuffer(0, 0, 0);
}
void update(long double delta) {
// updating m_cbCamera
dxgl::buffer::CameraBuffer camBuff{};
camBuff.position = m_camera->world().getTranslation();
camBuff.direction = m_camera->world().getZDirection();
m_cbCamera->update(&camBuff);
// updating m_cbPostProcess
ConstantBuffer dvbuff{};
dvbuff.time = t / 4.0f;
dvbuff.period = 0;// 8.0f;
dvbuff.amplitude = 0;// 0.0125f;
m_cbPostProcess->update(&dvbuff);
}
Here's my shader code. I'm trying to implement PBR.
VERTEX SHADER
struct VS_Input {
float4 position: POSITION;
float2 texcoord: TEXCOORD;
float3 normal: NORMAL;
float3 tangent: TANGENT;
};
struct PS_Input {
float4 position: SV_POSITION;
float2 texcoord: TEXCOORD;
float3 normal: NORMAL;
float3 tangent: TANGENT;
float3 pixelPosition: POSITION;
};
cbuffer transform: register(b0) {
row_major float4x4 world;
row_major float4x4 view;
row_major float4x4 proj;
};
PS_Input main(VS_Input input) {
PS_Input output = (PS_Input)0;
output.position = mul(input.position, world);
output.position = mul(output.position, view);
output.position = mul(output.position, proj);
output.pixelPosition = mul(input.position, world);
output.texcoord = input.texcoord;
output.normal = mul(input.normal, world);
output.tangent = mul(input.tangent, world);
return output;
}
PIXEL SHADER
struct PS_Input {
float4 position: SV_POSITION;
float2 texcoord: TEXCOORD;
float3 normal: NORMAL;
float3 tangent: TANGENT;
float3 pixelPosition: POSITION;
};
cbuffer camera: register(b0) {
float4 cameraPosition;
float4 cameraDirection;
}
float PI = 3.14159265359f;
SamplerState textureSampler: register(s0);
Texture2D tex_normalMap: register(t0);
Texture2D tex_heightMap: register(t1);
Texture2D tex_albedoMap: register(t2);
Texture2D tex_metallicMap: register(t3);
Texture2D tex_roughnessMap: register(t4);
Texture2D tex_aoMap: register(t5);
float3 calculateNormals(PS_Input input) {
float3 normal = normalize(input.normal);
float3 tangent = normalize(input.tangent);
tangent = normalize(input.tangent - dot(input.tangent, input.normal) * input.normal);
float3 bitangent = normalize(cross(normal, tangent));
const float3x3 TBN = float3x3(tangent, bitangent, normal);
float3 normalSample = tex_normalMap.Sample(textureSampler, input.texcoord).rgb;
normalSample.x = 2.0f * normalSample.r - 1.0f;
normalSample.y = -2.0f * normalSample.g + 1.0f;
normalSample.z = normalSample.b;
normal = mul(normalSample, TBN);
return normal;
}
float3 fresnelSchlick(float cosTheta, float3 F0) {
if (cosTheta > 1.0f) {
cosTheta = 1.0f;
}
float p = pow(1.0f - cosTheta, 5.0f);
return F0 + (1.0f - F0) * p;
}
float DistributionGGX(float3 N, float3 H, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0f);
float NdotH2 = NdotH * NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0f) + 1.0f);
denom = PI * denom * denom;
return num / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0f);
float k = (r * r) / 8.0f;
float num = NdotV;
float denom = NdotV * (1.0f - k) + k;
return num / denom;
}
float GeometrySmith(float3 N, float3 V, float3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0f);
float NdotL = max(dot(N, L), 0.0f);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
float3 getPBRLighting(PS_Input input) {
float3 color = float3(0.0f, 0.0f, 0.0f);
float3 albedoSample = pow(tex_albedoMap.Sample(textureSampler, input.texcoord).rgb, 2.2333f);
float metallicSample = tex_metallicMap.Sample(textureSampler, input.texcoord).r;
float roughnessSample = tex_roughnessMap.Sample(textureSampler, input.texcoord).r;
float aoSample = tex_aoMap.Sample(textureSampler, input.texcoord).r;
float3 N = normalize(input.normal);
float3 V = normalize(cameraPosition - input.pixelPosition);
float3 F0 = float3(0.04f, 0.04f, 0.04f);
F0 = lerp(F0, albedoSample, metallicSample);
float3 lightPositions[6] = {
float3(-12.0f, 0.0f, -3.0f),
float3( -6.0f, 0.0f, -3.0f),
float3( 0.0f, 0.0f, -3.0f),
float3( 6.0f, 0.0f, -3.0f),
float3( 12.0f, 0.0f, -3.0f),
float3( 18.0f, 0.0f, -3.0f),
};
float3 lightColor = float3(1.0f, 1.0f, 1.0f);
// reflectance equation
float3 Lo = float3(0.0f, 0.0f, 0.0f);
for (int i = 0; i < 6; i++) {
// calculate per-light radiance
float3 L = normalize(lightPositions[i] - input.pixelPosition);
float3 H = normalize(V + L);
float distance = length(lightPositions[i] - input.pixelPosition);
float attenuation = 1.0f / (distance * distance);
float3 radiance = lightColor * attenuation;
// cook-torrance brdf
float NDF = DistributionGGX(N, H, roughnessSample);
float G = GeometrySmith(N, V, L, roughnessSample);
float3 F = fresnelSchlick(max(dot(H, V), 0.0f), F0);
float3 kS = F;
float3 kD = float3(1.0f, 1.0f, 1.0f) - kS;
kD *= 1.0f - metallicSample;
float3 numerator = NDF * G * F;
float denominator = 4.0f * max(dot(N, V), 0.0f) * max(dot(N, L), 0.0f) + 0.0001f;
float3 specular = numerator / denominator;
// add to outgoing radiance Lo
float NdotL = max(dot(N, L), 0.0f);
Lo += (kD * albedoSample / PI + specular) * radiance * NdotL;
}
float3 ambient = float3(0.01f, 0.01f, 0.01f) * albedoSample * aoSample;
color = ambient + Lo;
return color;
}
float4 main(PS_Input input) : SV_TARGET {
input.normal = calculateNormals(input);
float3 color = getPBRLighting(input);
// HDR tone mapping and gamma correction
float exposure = 10.0f;
float gamma = 1.0f;
float3 toneMap = float3(1.0f, 1.0f, 1.0f) - exp((-color) * exposure);
float3 toneMappedColor = pow(toneMap, float3(gamma, gamma, gamma));
return float4(toneMappedColor, 1.0f);
}
Everything always compiles. I can not figure out for the life of me why the constant buffers aren't functioning properly.
Result with m_cbPostProcess
included:
https://gyazo.com/22bc8169fc5e3e772f4dafac2a78e4ed
Result commented out: https://gyazo.com/07359dd66e1d4a9dce55677b046c657d
I know I've included a lot of code here, but I figured more is better than less. Any help or suggestions are appreciated.
If a variable is not declared static const, it is automatically embedded in a "hidden" constant buffer (which is internally called $Globals) (and it will use the first available slot).
So if I take this code :
cbuffer cb_color : register(b0)
{
float4 color;
}
float myValue = 12.0f;
float4 PS(float4 position_screen : SV_Position) : SV_Target
{
return color*myValue;
};
is equivalent to
cbuffer cb_color : register(b0)
{
float4 color;
}
cbuffer cb_globals : register(b1)
{
float myValue = 12.0f;
}
float4 PS(float4 position_screen : SV_Position) : SV_Target
{
return color*myValue;
};
here it uses b1 as b0 is already explicitly used.
please also note that any variable not declared in a constant buffer will be merged into a single buffer:
cbuffer cb_color : register(b0)
{
float4 color;
}
float myValue = 12.0f;
float4 myOtherValue;
float4 PS(float4 position_screen : SV_Position) : SV_Target
{
return color*myValue*myOtherValue;
};
will be equivalent to :
cbuffer cb_color : register(b0)
{
float4 color;
}
cbuffer cb_globals : register(b1)
{
float myValue = 12.0f;
float3 pad0; //cbuffer members alignment rules
float4 myOtherValue;
}
float4 PS(float4 position_screen : SV_Position) : SV_Target
{
return color*myValue*myOtherValue;
};