From bcde2e80ffb202b4ee41c65e38749f0bf62e376e Mon Sep 17 00:00:00 2001 From: Szymon Nowakowski Date: Sun, 9 Nov 2025 21:32:13 +0100 Subject: [PATCH] PBR Shader --- src/main.zig | 13 +-- src/shader.glsl | 207 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 204 insertions(+), 16 deletions(-) diff --git a/src/main.zig b/src/main.zig index 55d19d0..bc49072 100644 --- a/src/main.zig +++ b/src/main.zig @@ -46,9 +46,10 @@ fn init() callconv(.c) void { bindings.vertex_buffers[0] = sg.makeBuffer(.{ .data = sg.asRange(&[_]f32{ - 0.0, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, - 0.5, -0.5, 0.5, 0.0, 1.0, 0.0, 1.0, - -0.5, -0.5, 0.5, 0.0, 0.0, 1.0, 1.0, + // positionOS texCoord normalOS tangentOS + 0.0, 0.5, 0.5, 0.5, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + 0.5, -0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, + -0.5, -0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, }), }); @@ -56,8 +57,10 @@ fn init() callconv(.c) void { .shader = sg.makeShader(shader.programShaderDesc(sg.queryBackend())), .layout = blk: { var ret: sg.VertexLayoutState = .{}; - ret.attrs[shader.ATTR_program_position_CS].format = .FLOAT3; - ret.attrs[shader.ATTR_program_color].format = .FLOAT4; + ret.attrs[shader.ATTR_program_positionOS].format = .FLOAT3; + ret.attrs[shader.ATTR_program_texCoord].format = .FLOAT2; + ret.attrs[shader.ATTR_program_normalOS].format = .FLOAT3; + ret.attrs[shader.ATTR_program_tangentOS].format = .FLOAT4; break :blk ret; }, }); diff --git a/src/shader.glsl b/src/shader.glsl index 9838005..34ba487 100644 --- a/src/shader.glsl +++ b/src/shader.glsl @@ -1,23 +1,208 @@ -@vs vert -in vec4 position_CS; -in vec4 color; +@vs vertex -out vec4 varColor; +layout(location = 0) in vec3 positionOS; +layout(location = 1) in vec2 texCoord; +layout(location = 2) in vec3 normalOS; +layout(location = 3) in vec4 tangentOS; + +layout(location = 0) out vec3 var_positionVS; +layout(location = 1) out vec2 var_texCoord; +layout(location = 2) out vec3 var_normalVS; +layout(location = 3) out vec3 var_tangentVS; +layout(location = 4) out vec3 var_bitangentVS; + +layout(binding = 0) uniform Global_Uniforms_Vertex { + mat4 _MatrixWStoVS; + mat4 _MatrixVStoCS; +}; + +layout(binding = 1) uniform Object_Uniforms { + mat4 _MatrixOStoWS; + mat4 _MatrixOStoWSNormal; +}; void main() { - gl_Position = position_CS; - varColor = color; + vec3 positionWS = (_MatrixOStoWS * vec4(positionOS, 1.0)).xyz; + vec3 positionVS = (_MatrixWStoVS * vec4(positionWS, 1.0)).xyz; + vec4 positionCS = _MatrixVStoCS * vec4(positionVS, 1.0); + + vec3 normalWS = normalize((_MatrixOStoWSNormal * vec4(normalOS, 0.0)).xyz); + vec3 normalVS = normalize((_MatrixWStoVS * vec4(normalWS, 0.0)).xyz); + + vec3 tangentWS = normalize((_MatrixOStoWS * vec4(tangentOS.xyz, 0.0)).xyz); + vec3 tangentVS = normalize((_MatrixWStoVS * vec4(tangentWS, 0.0)).xyz); + + vec3 bitangentVS = tangentOS.w * normalize(cross(normalVS, tangentVS)); + + gl_Position = positionCS; + var_positionVS = positionVS; + var_texCoord = texCoord; + var_normalVS = normalVS; + var_tangentVS = tangentVS; + var_bitangentVS = bitangentVS; } @end -@fs frag -in vec4 varColor; +@fs fragment + +struct Point_Light { + vec3 positionWS; + vec3 color; +}; + +struct Directional_Light { + vec3 directionWS; + vec3 color; +}; + +layout(location = 0) in vec3 var_positionVS; +layout(location = 1) in vec2 var_texCoord; +layout(location = 2) in vec3 var_normalVS; +layout(location = 3) in vec3 var_tangentVS; +layout(location = 4) in vec3 var_bitangentVS; out vec4 fragColor; -void main() { - fragColor = varColor; +layout(binding = 2) uniform Global_Uniforms_Fragment { + mat4 _MatrixWStoVS; + vec3 _AmbientLight; + int _PointLightCount; + int _DirectionalLightCount; +}; + +layout(binding = 3) uniform Material_Uniforms { + int _TextureIndex; +}; + +layout(binding = 0) readonly buffer Point_Lights { + Point_Light _PointLights[]; +}; + +layout(binding = 1) readonly buffer Directional_Lights { + Directional_Light _DirectionalLights[]; +}; + +layout(binding = 2) uniform texture2DArray _BaseColorTexture; +layout(binding = 3) uniform texture2DArray _OcclusionRoughnessMetallicTexture; +layout(binding = 4) uniform texture2DArray _NormalTexture; + +layout(binding = 0) uniform sampler _Sampler; + +const float INV_PI = 0.31830987; +const float IOR = 1.45; +const vec3 DIELECTRIC_F0 = vec3(pow((IOR - 1.0) / (IOR + 1.0), 2.0)); +const vec3 F90 = vec3(1.0); + +vec3 fresnelSchlick(float dotVH, vec3 f0) { + return mix(f0, F90, pow(1.0 - dotVH, 5.0)); } + +float visibilityGGX(float dotNL, float dotNV, float alpha) { + float alphaSquared = alpha * alpha; + + float vGGX = dotNL * sqrt(dotNV * dotNV * (1.0 - alphaSquared) + alphaSquared); + float lGGX = dotNV * sqrt(dotNL * dotNL * (1.0 - alphaSquared) + alphaSquared); + float GGX = vGGX + lGGX; + return mix(0.0, 0.5 / GGX, GGX > 0.0); +} + +float distributionGGX(float dotNH, float alpha) { + float alphaSquared = alpha * alpha; + float tmp = dotNH * dotNH * (alphaSquared - 1.0) + 1.0; + return alphaSquared * INV_PI / (tmp * tmp); +} + +vec3 toneMapAcesNarkowicz(vec3 color) { + const float A = 2.51; + const float B = 0.03; + const float C = 2.43; + const float D = 0.59; + const float E = 0.14; + return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0); +} + +vec3 lightOutgoingRadiance( + vec3 viewDirectionVS, vec3 normalVS, float dotNV, + vec3 baseColor, float alpha, float metallic, vec3 f0, + vec3 incomingRadiance, vec3 lightDirectionVS +) { + vec3 halfVectorVS = normalize(lightDirectionVS + viewDirectionVS); + float dotVH = clamp(dot(viewDirectionVS, halfVectorVS), 0.0, 1.0); + float dotNH = clamp(dot(normalVS, halfVectorVS), 0.0, 1.0); + float dotNL = clamp(dot(normalVS, lightDirectionVS), 0.0, 1.0); + + vec3 fresnel = fresnelSchlick(dotVH, f0); + float visibility = visibilityGGX(dotNL, dotNV, alpha); + float distribution = distributionGGX(dotNH, alpha); + + vec3 scatteredFactor = (1.0 - fresnel) * (1.0 - metallic) * baseColor * INV_PI; + vec3 reflectedFactor = fresnel * visibility * distribution; + + return (scatteredFactor + reflectedFactor) * incomingRadiance * dotNL; +} + +void main() { + vec4 baseColorTexel = texture(sampler2DArray(_BaseColorTexture, _Sampler), vec3(var_texCoord, float(_TextureIndex))); + vec4 occlusionRoughnessMetallicTexel = texture(sampler2DArray(_OcclusionRoughnessMetallicTexture, _Sampler), vec3(var_texCoord, float(_TextureIndex))); + vec4 normalTextureTexel = texture(sampler2DArray(_NormalTexture, _Sampler), vec3(var_texCoord, float(_TextureIndex))); + + vec3 baseColor = baseColorTexel.rgb; + float occlusion = occlusionRoughnessMetallicTexel.r; + float roughness = occlusionRoughnessMetallicTexel.g; + float metallic = occlusionRoughnessMetallicTexel.b; + + vec3 tangentVS = normalize(var_tangentVS); + vec3 bitangentVS = normalize(var_bitangentVS); + mat3 matrixTStoVS = mat3(tangentVS, bitangentVS, var_normalVS); + vec3 normalTS = normalTextureTexel.xyz * 2.0 - 1.0; + vec3 normalVS = normalize(matrixTStoVS * normalTS); + + vec3 positionVS = var_positionVS; + vec3 viewDirectionVS = normalize(-positionVS); + float dotNV = clamp(dot(normalVS, viewDirectionVS), 0.0, 1.0); + float alpha = roughness * roughness; + + vec3 f0 = mix(DIELECTRIC_F0, baseColor, metallic); + + vec3 outgoingRadiance = vec3(0.0); + + for (int i = 0; i < _PointLightCount; i++) { + Point_Light light = _PointLights[i]; + + vec3 lightPositionVS = (_MatrixWStoVS * vec4(light.positionWS, 1.0)).xyz; + vec3 lightDirectionVS = normalize(lightPositionVS - positionVS); + float lightDistance = distance(positionVS, lightPositionVS); + float lightAttenuation = 1.0 / (lightDistance * lightDistance); + vec3 incomingRadiance = light.color * lightAttenuation; + + outgoingRadiance += lightOutgoingRadiance( + viewDirectionVS, normalVS, dotNV, + baseColor, alpha, metallic, f0, + incomingRadiance, lightDirectionVS + ); + } + + for (int i = 0; i < _DirectionalLightCount; i++) { + Directional_Light light = _DirectionalLights[i]; + + vec3 lightDirectionVS = normalize((_MatrixWStoVS * vec4(light.directionWS, 0.0)).xyz); + vec3 incomingRadiance = light.color; + + outgoingRadiance += lightOutgoingRadiance( + viewDirectionVS, normalVS, dotNV, + baseColor, alpha, metallic, f0, + incomingRadiance, lightDirectionVS + ); + } + + outgoingRadiance += _AmbientLight * baseColor * occlusion; + + vec3 toneMappedLinearColor = toneMapAcesNarkowicz(outgoingRadiance); + vec3 toneMappedSrgbColor = pow(toneMappedLinearColor, vec3(1.0 / 2.2)); + + fragColor = vec4(toneMappedSrgbColor, 1.0); +} + @end -@program program vert frag +@program program vertex fragment