Skip to content

Instantly share code, notes, and snippets.

@michidk
Last active March 14, 2021 14:41
Show Gist options
  • Save michidk/6c07cd38575ada149037d98989d9da37 to your computer and use it in GitHub Desktop.
Save michidk/6c07cd38575ada149037d98989d9da37 to your computer and use it in GitHub Desktop.
SHADERRed BRDF Shader
<?xml version="1.0"?>
<project version="2">
<pipeline>
<pass name="BRDF" type="shader" active="true" patchverts="1">
<shader type="vs" path="shaders\vertex.vk" entry="main" />
<shader type="ps" path="shaders\fragment.vk" entry="main" />
<inputlayout>
<item value="Position" semantic="POSITION" />
<item value="Normal" semantic="NORMAL" />
<item value="Texcoord" semantic="TEXCOORD0" />
</inputlayout>
<rendertexture />
<items>
<item name="Sphere" type="geometry">
<type>Sphere</type>
<width>1</width>
<height>1</height>
<depth>1</depth>
<topology>TriangleList</topology>
</item>
</items>
<itemvalues>
<value variable="u_MaterialData.color" for="Sphere">
<row>
<value>0.876447856</value>
<value>0.514363229</value>
<value>0.514363229</value>
<value>0</value>
</row>
</value>
<value variable="u_MaterialData.metallic" for="Sphere">
<row>
<value>0</value>
</row>
</value>
<value variable="u_MaterialData.roughness" for="Sphere">
<row>
<value>0.100000001</value>
</row>
</value>
</itemvalues>
<variables>
<variable type="float4x4" name="u_FrameData.view_matrix" system="View" />
<variable type="float4x4" name="u_FrameData.projection_matrix" system="Projection" />
<variable type="float4x4" name="u_FrameData.modelMatrix" system="GeometryTransform" />
<variable type="float4x4" name="u_FrameData.inverseModelMatrix" invert="true" system="GeometryTransform" />
<variable type="float3" name="u_FrameData.cameraCoordinates" system="CameraPosition3" />
<variable type="float4" name="u_MaterialData.color">
<row>
<value>1</value>
<value>0.999989986</value>
<value>0.999989986</value>
<value>0</value>
</row>
</variable>
<variable type="float" name="u_MaterialData.metallic">
<row>
<value>0</value>
</row>
</variable>
<variable type="float" name="u_MaterialData.roughness">
<row>
<value>0</value>
</row>
</variable>
</variables>
<macros />
</pass>
</pipeline>
<objects />
<cameras />
<settings>
<entry type="property" name="Sphere" item="pipe" />
<entry type="file" name="BRDF" shader="vs" />
<entry type="file" name="BRDF" shader="ps" />
<entry type="pinned" name="u_MaterialData.color" owner="BRDF" />
<entry type="pinned" name="u_MaterialData.metallic" owner="BRDF" />
<entry type="pinned" name="u_MaterialData.roughness" owner="BRDF" />
<entry type="camera" fp="false">
<distance>14</distance>
<pitch>36</pitch>
<yaw>233</yaw>
<roll>360</roll>
</entry>
<entry type="clearcolor" r="0" g="0" b="0" a="0" />
<entry type="usealpha" val="false" />
</settings>
<plugindata />
</project>
#version 450
layout (location = 0) in vec3 i_normal;
layout (location = 1) in vec4 i_worldpos;
layout (binding = 0) uniform FrameData
{
mat4 view_matrix;
mat4 projection_matrix;
mat4 modelMatrix;
mat4 inverseModelMatrix;
vec3 cameraCoordinates;
} u_FrameData;
layout (binding = 1) uniform MaterialData {
vec4 color;
float metallic;
float roughness;
} u_MaterialData;
layout (location = 0) out vec4 o_color;
const float PI = 3.1415926535897932384626433832795;
// normal distribution function: Trowbridge-Reitz GGX
float distributionGGX(vec3 normal, vec3 halfVector, float roughness) {
float a = roughness * roughness; // rougness apparently looks more "correct", when beeing squared (according to Disney)
float a2 = a * a;
float nDotH = max(dot(normal, halfVector), 0.0);
float nDotH2 = nDotH * nDotH;
float denom = (nDotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return a2 / denom;
}
// geometry function: Schlick-GGX
float geometrySchlickGGX(float vec, float roughness) {
float r = roughness + 1.0;
float k = (r * r) / 8.0;
float denom = vec * (1.0 - k) + k;
return vec / denom;
}
// approximate geometry: account for view dir and light dir: Smith's method
float geometrySmith(float nDotV, float nDotL, float roughness) {
return geometrySchlickGGX(nDotL, roughness) * geometrySchlickGGX(nDotV, roughness);
}
// Schlicks approximation
vec3 schlick(vec3 r0, float cosTheta) {
// we could use pow, but then it do all the float checks - which we don't need
return r0 + (1.0 - r0) * (1.0 - cosTheta) * (1.0 - cosTheta) * (1.0 - cosTheta) * (1.0 - cosTheta) * (1.0 - cosTheta);
}
vec3 computeRadiance(vec3 irradiance, vec3 lightDirection, vec3 normal, vec3 cameraDirection, vec3 surfaceColor) {
float metallic = u_MaterialData.metallic;
float roughness = u_MaterialData.roughness;
// utils
vec3 halfVector = normalize(cameraDirection + lightDirection);
float nDotH = max(dot(normal, halfVector), 0.0);
float nDotL = max(dot(normal, lightDirection), 0.0);
float hDotV = max(dot(halfVector, cameraDirection), 0.0);
float nDotV = max(dot(normal, cameraDirection), 0.0);
vec3 f0 = mix(vec3(0.04), surfaceColor, vec3(metallic)); // base relectivity: use 0.04 for non-metallic/dialectic materials else use the surface color
vec3 f = schlick(f0, hDotV);
float ndf = distributionGGX(normal, halfVector, roughness);
float geometry = geometrySmith(nDotV, nDotL, roughness);
// Cook-Torrance BRDF
vec3 numerator = ndf * geometry * f;
float denominator = 4.0 * nDotV * nDotL;
vec3 specular = numerator / max(denominator, 0.001);
vec3 kS = f; // energy of light that gets reflected
vec3 kD = vec3(1.0) - kS; // remaining light that gets refracted
kD *= 1.0 - metallic; // metalls don't refract, so set it to 0 if it's a metal
return (kD * surfaceColor / PI + specular) * irradiance * nDotL;
}
// irrandiance: light color in lx (= lm/m^2), values from https://en.wikipedia.org/wiki/Lux#Illuminance
vec3 directionalLight(vec3 directionToLight, vec3 irradiance, vec3 directionToCamera) {
return computeRadiance(vec3(10.1, 10.1, 10.1), normalize(vec3(1, 1, 0)), normalize(i_normal), directionToCamera, u_MaterialData.color.xyz);
}
// luminousFlux: light color in lm, values from https://en.wikipedia.org/wiki/Luminous_flux#Examples
vec3 pointLight(vec3 lightPosition, vec3 luminousFlux, vec3 directionToCamera) {
// light fall-off
vec3 directionToLight = normalize(lightPosition - i_worldpos.xyz);
float d = length(i_worldpos.xyz - lightPosition);
vec3 irradiance = luminousFlux / (4 * PI * d * d);
return computeRadiance(irradiance, directionToLight, normalize(i_normal), directionToCamera, u_MaterialData.color.xyz);
}
void main() {
vec3 radiance = vec3(0);
vec3 i_normal = normalize(i_normal);
vec3 directionToCamera = normalize(u_FrameData.cameraCoordinates - i_worldpos.xyz);
vec3 color = u_MaterialData.color.xyz;
// directional lights
radiance += directionalLight(vec3(0, 1, 0), vec3(10.1, 10.1, 10.1), directionToCamera);
// point lights
radiance += pointLight(vec3(0.1, -3, -3), vec3(100, 100, 100), directionToCamera);
radiance = radiance / (vec3(1.0) + radiance); // Reinhard tone mapping
o_color = vec4(radiance, 1.0);
}
#version 450
layout (binding = 0) uniform FrameData
{
mat4 view_matrix;
mat4 projection_matrix;
mat4 modelMatrix;
mat4 inverseModelMatrix;
vec3 cameraCoordinates;
} u_FrameData;
layout (location = 0) in vec3 i_position;
layout (location = 1) in vec3 i_normal;
layout (location = 0) out vec3 o_normal;
layout (location = 1) out vec4 o_worldPos;
void main() {
o_worldPos = u_FrameData.modelMatrix * vec4(i_position, 1);
gl_Position = u_FrameData.projection_matrix * u_FrameData.view_matrix * o_worldPos;
o_normal = mat3(transpose(u_FrameData.inverseModelMatrix)) * i_normal;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment