Skip to content

Commit 6d6dc85

Browse files
committed
specular ibl
1 parent eb7a635 commit 6d6dc85

23 files changed

+395
-49
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# bunny-renderer
22

3-
<img src="screenshots/sponza.png"/>
3+
<img src="screenshots/sponza.png"/>
4+
5+
<img src="screenshots/cerberus.png"/>

res/shaders/editor/brdf.frag

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#version 330 core
2+
out vec2 FragColor;
3+
in vec2 TexCoords;
4+
5+
uniform samplerCube environmentMap;
6+
uniform float roughness;
7+
8+
const float PI = 3.14159265359;
9+
10+
float RadicalInverse_VdC(uint bits)
11+
{
12+
bits = (bits << 16u) | (bits >> 16u);
13+
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
14+
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
15+
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
16+
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
17+
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
18+
}
19+
// ----------------------------------------------------------------------------
20+
vec2 Hammersley(uint i, uint N)
21+
{
22+
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
23+
}
24+
25+
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
26+
{
27+
float a = roughness*roughness;
28+
29+
float phi = 2.0 * PI * Xi.x;
30+
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
31+
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
32+
33+
// from spherical coordinates to cartesian coordinates
34+
vec3 H;
35+
H.x = cos(phi) * sinTheta;
36+
H.y = sin(phi) * sinTheta;
37+
H.z = cosTheta;
38+
39+
// from tangent-space vector to world-space sample vector
40+
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
41+
vec3 tangent = normalize(cross(up, N));
42+
vec3 bitangent = cross(N, tangent);
43+
44+
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
45+
return normalize(sampleVec);
46+
}
47+
48+
float GeometrySchlickGGX(float NdotV, float roughness)
49+
{
50+
float a = roughness;
51+
float k = (a * a) / 2.0;
52+
53+
float nom = NdotV;
54+
float denom = NdotV * (1.0 - k) + k;
55+
56+
return nom / denom;
57+
}
58+
// ----------------------------------------------------------------------------
59+
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
60+
{
61+
float NdotV = max(dot(N, V), 0.0);
62+
float NdotL = max(dot(N, L), 0.0);
63+
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
64+
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
65+
66+
return ggx1 * ggx2;
67+
}
68+
69+
vec2 IntegrateBRDF(float NdotV, float roughness)
70+
{
71+
vec3 V;
72+
V.x = sqrt(1.0 - NdotV*NdotV);
73+
V.y = 0.0;
74+
V.z = NdotV;
75+
76+
float A = 0.0;
77+
float B = 0.0;
78+
79+
vec3 N = vec3(0.0, 0.0, 1.0);
80+
81+
const uint SAMPLE_COUNT = 1024u;
82+
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
83+
{
84+
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
85+
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
86+
vec3 L = normalize(2.0 * dot(V, H) * H - V);
87+
88+
float NdotL = max(L.z, 0.0);
89+
float NdotH = max(H.z, 0.0);
90+
float VdotH = max(dot(V, H), 0.0);
91+
92+
if(NdotL > 0.0)
93+
{
94+
float G = GeometrySmith(N, V, L, roughness);
95+
float G_Vis = (G * VdotH) / (NdotH * NdotV);
96+
float Fc = pow(1.0 - VdotH, 5.0);
97+
98+
A += (1.0 - Fc) * G_Vis;
99+
B += Fc * G_Vis;
100+
}
101+
}
102+
A /= float(SAMPLE_COUNT);
103+
B /= float(SAMPLE_COUNT);
104+
return vec2(A, B);
105+
}
106+
// ----------------------------------------------------------------------------
107+
void main()
108+
{
109+
vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y);
110+
FragColor = integratedBRDF;
111+
}

res/shaders/editor/brdf.vert

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#version 330 core
2+
layout (location = 0) in vec3 aPos;
3+
layout (location = 1) in vec2 aTexCoords;
4+
5+
out vec2 TexCoords;
6+
7+
void main()
8+
{
9+
TexCoords = aTexCoords;
10+
gl_Position = vec4(aPos, 1.0);
11+
}

res/shaders/editor/ibl.frag res/shaders/editor/irradiance.frag

+2-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ void main()
2828
// tangent space to world
2929
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;
3030

31-
irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
31+
vec3 envColor = texture(environmentMap, sampleVec).rgb;
32+
irradiance += pow(envColor, vec3(2.2)) * cos(theta) * sin(theta);
3233
nrSamples++;
3334
}
3435
}
File renamed without changes.

res/shaders/editor/prefilter.frag

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#version 330 core
2+
out vec4 FragColor;
3+
in vec3 localPos;
4+
5+
uniform samplerCube environmentMap;
6+
uniform float roughness;
7+
8+
const float PI = 3.14159265359;
9+
10+
float RadicalInverse_VdC(uint bits)
11+
{
12+
bits = (bits << 16u) | (bits >> 16u);
13+
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
14+
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
15+
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
16+
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
17+
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
18+
}
19+
// ----------------------------------------------------------------------------
20+
vec2 Hammersley(uint i, uint N)
21+
{
22+
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
23+
}
24+
25+
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
26+
{
27+
float a = roughness*roughness;
28+
29+
float phi = 2.0 * PI * Xi.x;
30+
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
31+
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
32+
33+
// from spherical coordinates to cartesian coordinates
34+
vec3 H;
35+
H.x = cos(phi) * sinTheta;
36+
H.y = sin(phi) * sinTheta;
37+
H.z = cosTheta;
38+
39+
// from tangent-space vector to world-space sample vector
40+
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
41+
vec3 tangent = normalize(cross(up, N));
42+
vec3 bitangent = cross(N, tangent);
43+
44+
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
45+
return normalize(sampleVec);
46+
}
47+
48+
void main()
49+
{
50+
vec3 N = normalize(localPos);
51+
vec3 R = N;
52+
vec3 V = R;
53+
54+
const uint SAMPLE_COUNT = 1024u;
55+
float totalWeight = 0.0;
56+
vec3 prefilteredColor = vec3(0.0);
57+
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
58+
{
59+
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
60+
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
61+
vec3 L = normalize(2.0 * dot(V, H) * H - V);
62+
63+
float NdotL = max(dot(N, L), 0.0);
64+
if(NdotL > 0.0)
65+
{
66+
vec3 envColor = texture(environmentMap, L).rgb;
67+
prefilteredColor += pow(envColor, vec3(2.2)) * NdotL;
68+
totalWeight += NdotL;
69+
}
70+
}
71+
prefilteredColor = prefilteredColor / totalWeight;
72+
73+
FragColor = vec4(prefilteredColor, 1.0);
74+
}

res/shaders/editor/prefilter.vert

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#version 330 core
2+
layout (location = 0) in vec3 aPos;
3+
4+
uniform mat4 projection;
5+
uniform mat4 view;
6+
7+
out vec3 localPos;
8+
9+
void main()
10+
{
11+
localPos = aPos;
12+
13+
mat4 rotView = mat4(mat3(view)); // remove translation from the view matrix
14+
vec4 pos = projection * rotView * vec4(aPos, 1.0f);
15+
16+
gl_Position = pos.xyww;
17+
}

res/shaders/normal.frag

+26-9
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ uniform sampler2D normalMap;
2424
uniform sampler2D metalMap;
2525
uniform sampler2D shadowMap;
2626

27+
// IBL
2728
uniform samplerCube irradianceMap;
29+
uniform samplerCube prefilterMap;
30+
uniform sampler2D brdfLUT;
2831

2932
float calcSoftShadow(vec3 normal)
3033
{
@@ -54,11 +57,6 @@ float calcSoftShadow(vec3 normal)
5457
return shadow;
5558
}
5659

57-
vec3 fresnelSchlick(float cosTheta, vec3 F0)
58-
{
59-
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
60-
}
61-
6260
const float PI = 3.14159265359;
6361

6462
float DistributionGGX(vec3 N, vec3 H, float roughness)
@@ -95,6 +93,16 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
9593
return ggx1 * ggx2;
9694
}
9795

96+
vec3 fresnelSchlick(float cosTheta, vec3 F0)
97+
{
98+
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
99+
}
100+
101+
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
102+
{
103+
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
104+
}
105+
98106
vec3 LightingPhysicallyBased(vec3 N, vec3 V, vec3 albedo, vec3 F0, float metallic, float roughness
99107
, vec3 lightColor, vec3 lightDirectionWS, float lightAttenuation)
100108
{
@@ -141,6 +149,7 @@ void main()
141149

142150
vec3 N = normal;
143151
vec3 V = normalize(_WorldSpaceCameraPos - posWorld);
152+
vec3 R = reflect(-V, N);
144153

145154
vec4 metalMapColor = texture(metalMap, texCoord);
146155
float metallic = metalMapColor.b * _Metallic;
@@ -151,12 +160,20 @@ void main()
151160

152161
vec3 color = LightingPhysicallyBased(normal, V, albedo.rgb, F0, metallic, roughness, _MainLightColor, _MainLightPosition, 1.0 - shadow);
153162

154-
// ibl diffuse
155-
vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
156-
vec3 kD = 1.0 - kS;
163+
vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
164+
vec3 kD = 1.0 - F;
157165
kD *= 1.0 - metallic;
158166
vec3 irradiance = texture(irradianceMap, N).rgb;
159-
vec3 ambient = kD * irradiance * albedo.rgb;
167+
vec3 diffuse = irradiance * albedo.rgb;
168+
169+
// sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part.
170+
const float MAX_REFLECTION_LOD = 4.0;
171+
vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb;
172+
vec2 brdf = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;
173+
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);
174+
175+
vec3 ambient = kD * diffuse + specular;
176+
160177
color += ambient;
161178

162179
color = color / (color + vec3(1.0)); // Reinhard tonemapping

res/shaders/skybox.frag

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ uniform samplerCube skybox;
77

88
void main()
99
{
10-
FragColor = texture(skybox, texCoords);
10+
vec3 envColor = textureLod(skybox, texCoords, 1.2).rgb;
11+
12+
FragColor = vec4(envColor, 1);
1113
}

res/shaders/skybox.vert

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ uniform mat4 view;
88

99
void main()
1010
{
11-
vec4 pos = projection * view * vec4(aPos, 1.0f);
12-
gl_Position = vec4(pos.x, pos.y, pos.w, pos.w);
1311
texCoords = vec3(aPos.x, aPos.y, -aPos.z);
12+
13+
mat4 rotView = mat4(mat3(view)); // remove translation from the view matrix
14+
vec4 pos = projection * rotView * vec4(aPos, 1.0f);
15+
16+
gl_Position = vec4(pos.x, pos.y, pos.w, pos.w);
1417
}

screenshots/cerberus.png

1.3 MB
Loading

screenshots/sponza.png

-118 KB
Loading

src/Application.h

+1
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ class Application
527527
if (ImGui::Button("Reload Shader"))
528528
{
529529
Shader::normalShader->ReloadShader();
530+
Shader::skyboxShader->ReloadShader();
530531
std::cout << "Reloaded Shaders." << std::endl;
531532
}
532533

src/common/Dialog.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66

77
const COMDLG_FILTERSPEC c_rgSaveTypes[] =
88
{
9-
//{L"PNG Files (*.png)", L"*.png"},
10-
{L"Image (*.png; *.tga; *.jpg)", L"*.png;*.tga;*.jpg"},
11-
{L"OBJ Files (*.obj)", L"*.obj"},
12-
{L"glTF Files (*.gltf)", L"*.gltf"},
13-
{L"All Documents (*.*)", L"*.*"}
9+
{ L"Image (*.png; *.tga; *.jpg)", L"*.png;*.tga;*.jpg" },
10+
{ L"Model Files (*.obj; *.fbx)", L"*.obj;*.fbx" },
11+
{ L"glTF Files (*.gltf)", L"*.gltf" },
12+
{ L"All Documents (*.*)", L"*.*" }
1413
};
1514

1615
std::string wstring2string(std::wstring wstr)

0 commit comments

Comments
 (0)