实现IBL

dev-VirtualTexture
wuyize 2023-02-13 17:12:04 +08:00
parent f3559d133d
commit 2a78bb4be8
20 changed files with 786 additions and 294 deletions

View File

@ -121,6 +121,7 @@
<ClCompile Include="src\main.cpp" /> <ClCompile Include="src\main.cpp" />
<ClCompile Include="src\MainWindow.cpp" /> <ClCompile Include="src\MainWindow.cpp" />
<ClCompile Include="src\NavigationBarWidget.cpp" /> <ClCompile Include="src\NavigationBarWidget.cpp" />
<ClCompile Include="src\Renderer\IblUtils.cpp" />
<ClCompile Include="src\Renderer\Painting\BaseStyle.cpp" /> <ClCompile Include="src\Renderer\Painting\BaseStyle.cpp" />
<ClCompile Include="src\Renderer\Painting\BvhTree.cpp" /> <ClCompile Include="src\Renderer\Painting\BvhTree.cpp" />
<ClCompile Include="src\Renderer\Camera.cpp" /> <ClCompile Include="src\Renderer\Camera.cpp" />
@ -154,13 +155,16 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\data.json" /> <None Include="..\data.json" />
<None Include="res\Shaders\brdf_lut.comp" />
<None Include="res\Shaders\cubemap.frag" /> <None Include="res\Shaders\cubemap.frag" />
<None Include="res\Shaders\cubemap.vert" /> <None Include="res\Shaders\cubemap.vert" />
<None Include="res\Shaders\cubemap_prefilter.frag" />
<None Include="res\Shaders\depth_init.comp" /> <None Include="res\Shaders\depth_init.comp" />
<None Include="res\Shaders\depth_mipmap.comp" /> <None Include="res\Shaders\depth_mipmap.comp" />
<None Include="res\Shaders\element.comp" /> <None Include="res\Shaders\element.comp" />
<None Include="res\Shaders\final.frag" /> <None Include="res\Shaders\final.frag" />
<None Include="res\Shaders\final.vert" /> <None Include="res\Shaders\final.vert" />
<None Include="res\Shaders\irradiance_convolution.frag" />
<None Include="res\Shaders\model.frag" /> <None Include="res\Shaders\model.frag" />
<None Include="res\Shaders\model.vert" /> <None Include="res\Shaders\model.vert" />
<None Include="res\Shaders\model_shadow.frag" /> <None Include="res\Shaders\model_shadow.frag" />
@ -192,6 +196,7 @@
<ClInclude Include="src\Editor\ThirdPartyLib\SvgHelper.h" /> <ClInclude Include="src\Editor\ThirdPartyLib\SvgHelper.h" />
<ClInclude Include="src\Editor\util\PainterPathUtil.h" /> <ClInclude Include="src\Editor\util\PainterPathUtil.h" />
<ClInclude Include="src\Editor\util\SvgFileLoader.h" /> <ClInclude Include="src\Editor\util\SvgFileLoader.h" />
<ClInclude Include="src\Renderer\IblUtils.h" />
<ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h" /> <ClInclude Include="src\Renderer\Painting\CubicBezierSignedDistance.h" />
<ClInclude Include="src\Renderer\Painting\Element.h" /> <ClInclude Include="src\Renderer\Painting\Element.h" />
<ClInclude Include="src\Renderer\Painting\ElementStyle.h" /> <ClInclude Include="src\Renderer\Painting\ElementStyle.h" />

View File

@ -210,6 +210,9 @@
<ClCompile Include="src\Editor\LayerStyle.cpp"> <ClCompile Include="src\Editor\LayerStyle.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Renderer\IblUtils.cpp">
<Filter>Source Files\Renderer</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="src\Renderer\RendererGLWidget.h"> <QtMoc Include="src\Renderer\RendererGLWidget.h">
@ -311,6 +314,15 @@
<None Include="res\Shaders\skybox.frag"> <None Include="res\Shaders\skybox.frag">
<Filter>Resource Files\Shaders</Filter> <Filter>Resource Files\Shaders</Filter>
</None> </None>
<None Include="res\Shaders\irradiance_convolution.frag">
<Filter>Resource Files\Shaders</Filter>
</None>
<None Include="res\Shaders\cubemap_prefilter.frag">
<Filter>Resource Files\Shaders</Filter>
</None>
<None Include="res\Shaders\brdf_lut.comp">
<Filter>Resource Files\Shaders</Filter>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtUic Include="EditorWidget.ui"> <QtUic Include="EditorWidget.ui">
@ -423,6 +435,9 @@
<ClInclude Include="src\Editor\PixelPath.h"> <ClInclude Include="src\Editor\PixelPath.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Renderer\IblUtils.h">
<Filter>Header Files\Renderer</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtRcc Include="res\MainWindow.qrc"> <QtRcc Include="res\MainWindow.qrc">

View File

@ -115,11 +115,20 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>12</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<item> <item>
<widget class="QSlider" name="horizontalSlider"> <widget class="QSlider" name="horizontalSlider">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
@ -128,7 +137,7 @@
<number>180</number> <number>180</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>
@ -138,7 +147,23 @@
<number>360</number> <number>360</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="exposureSlider">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -28,6 +28,9 @@
<file>Shaders/cubemap.vert</file> <file>Shaders/cubemap.vert</file>
<file>Shaders/skybox.frag</file> <file>Shaders/skybox.frag</file>
<file>Shaders/skybox.vert</file> <file>Shaders/skybox.vert</file>
<file>Shaders/irradiance_convolution.frag</file>
<file>Shaders/cubemap_prefilter.frag</file>
<file>Shaders/brdf_lut.comp</file>
</qresource> </qresource>
<qresource prefix="/qt/etc"> <qresource prefix="/qt/etc">
<file>qt.conf</file> <file>qt.conf</file>

View File

@ -0,0 +1,116 @@
#version 450 core
layout(local_size_x = 8, local_size_y = 8) in;
layout(rg16f, binding = 0) uniform image2D brdfLUT;
const float PI = 3.14159265359;
// ----------------------------------------------------------------------------
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// efficient VanDerCorpus calculation.
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
// ----------------------------------------------------------------------------
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
{
float a = roughness*roughness;
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
// from spherical coordinates to cartesian coordinates - halfway vector
vec3 H;
H.x = cos(phi) * sinTheta;
H.y = sin(phi) * sinTheta;
H.z = cosTheta;
// from tangent-space H vector to world-space sample vector
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, N));
vec3 bitangent = cross(N, tangent);
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
return normalize(sampleVec);
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness)
{
// note that we use a different k for IBL
float a = roughness;
float k = (a * a) / 2.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec2 IntegrateBRDF(float NdotV, float roughness)
{
vec3 V;
V.x = sqrt(1.0 - NdotV*NdotV);
V.y = 0.0;
V.z = NdotV;
float A = 0.0;
float B = 0.0;
vec3 N = vec3(0.0, 0.0, 1.0);
const uint SAMPLE_COUNT = 1024u;
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
{
// generates a sample vector that's biased towards the
// preferred alignment direction (importance sampling).
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
vec3 L = normalize(2.0 * dot(V, H) * H - V);
float NdotL = max(L.z, 0.0);
float NdotH = max(H.z, 0.0);
float VdotH = max(dot(V, H), 0.0);
if(NdotL > 0.0)
{
float G = GeometrySmith(N, V, L, roughness);
float G_Vis = (G * VdotH) / (NdotH * NdotV);
float Fc = pow(1.0 - VdotH, 5.0);
A += (1.0 - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
A /= float(SAMPLE_COUNT);
B /= float(SAMPLE_COUNT);
return vec2(A, B);
}
// ----------------------------------------------------------------------------
void main()
{
uvec2 pixelLocation = gl_GlobalInvocationID.xy;
vec2 texCoords = vec2(pixelLocation)/vec2(imageSize(brdfLUT));
vec2 integratedBRDF = IntegrateBRDF(texCoords.x, texCoords.y);
imageStore(brdfLUT, ivec2(pixelLocation), vec4(integratedBRDF,0,0));
}

View File

@ -0,0 +1,107 @@
#version 450 core
out vec4 FragColor;
in vec3 WorldPos;
uniform samplerCube environmentMap;
uniform float roughness;
const float PI = 3.14159265359;
// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
// ----------------------------------------------------------------------------
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// efficient VanDerCorpus calculation.
float RadicalInverse_VdC(uint bits)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
}
// ----------------------------------------------------------------------------
vec2 Hammersley(uint i, uint N)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
}
// ----------------------------------------------------------------------------
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
{
float a = roughness*roughness;
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
// from spherical coordinates to cartesian coordinates - halfway vector
vec3 H;
H.x = cos(phi) * sinTheta;
H.y = sin(phi) * sinTheta;
H.z = cosTheta;
// from tangent-space H vector to world-space sample vector
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, N));
vec3 bitangent = cross(N, tangent);
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
return normalize(sampleVec);
}
// ----------------------------------------------------------------------------
void main()
{
vec3 N = normalize(WorldPos);
// make the simplyfying assumption that V equals R equals the normal
vec3 R = N;
vec3 V = R;
const uint SAMPLE_COUNT = 1024u;
vec3 prefilteredColor = vec3(0.0);
float totalWeight = 0.0;
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
{
// generates a sample vector that's biased towards the preferred alignment direction (importance sampling).
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
vec3 L = normalize(2.0 * dot(V, H) * H - V);
float NdotL = max(dot(N, L), 0.0);
if(NdotL > 0.0)
{
// sample from the environment's mip level based on roughness/pdf
float D = DistributionGGX(N, H, roughness);
float NdotH = max(dot(N, H), 0.0);
float HdotV = max(dot(H, V), 0.0);
float pdf = D * NdotH / (4.0 * HdotV) + 0.0001;
//float resolution = 512.0; // resolution of source cubemap (per face)
float resolution = float(textureSize(environmentMap,0).x); // resolution of source cubemap (per face)
float saTexel = 4.0 * PI / (6.0 * resolution * resolution);
float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001);
float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
prefilteredColor += textureLod(environmentMap, L, mipLevel).rgb * NdotL;
totalWeight += NdotL;
}
}
prefilteredColor = prefilteredColor / totalWeight;
FragColor = vec4(prefilteredColor, 1.0);
}

View File

@ -5,58 +5,16 @@ out vec4 FragColor;
in vec2 TexCoords; in vec2 TexCoords;
uniform sampler2D gBaseColor; uniform sampler2D gBaseColor;
uniform sampler2D gNormal; uniform float exposure = 1;
uniform sampler2D gPosition;
uniform sampler2D gMetallicRoughness;
uniform sampler2D gDepth;
uniform sampler2D gDirectLight;
uniform sampler2D gIndirectLight;
uniform vec3 mainLightDirection; vec3 ACESToneMapping(vec3 color)
uniform vec3 mainLightRadiance;
uniform vec3 camPos;
const float PI = 3.14159265359;
float DistributionGGX(vec3 N, vec3 H, float roughness)
{ {
float a = roughness*roughness; const float A = 2.51;
float a2 = a*a; const float B = 0.03;
float NdotH = max(dot(N, H), 0.0); const float C = 2.43;
float NdotH2 = NdotH*NdotH; const float D = 0.59;
const float E = 0.14;
float nom = a2; return (color * (A * color + B)) / (color * (C * color + D) + E);
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
} }
void main() void main()
@ -81,7 +39,11 @@ void main()
discard; discard;
} }
vec3 color = 8 * rgbm.rgb * rgbm.a; vec3 color = 8 * rgbm.rgb * rgbm.a;
color = color / (color + vec3(1.0));
color *= exposure;
color = ACESToneMapping(color);
//color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2)); color = pow(color, vec3(1.0/2.2));
FragColor = vec4(color, 1.0); FragColor = vec4(color, 1.0);
return; return;

View File

@ -0,0 +1,43 @@
#version 450 core
out vec4 FragColor;
in vec3 WorldPos;
uniform samplerCube environmentMap;
const float PI = 3.14159265359;
void main()
{
// The world vector acts as the normal of a tangent surface
// from the origin, aligned to WorldPos. Given this normal, calculate all
// incoming radiance of the environment. The result of this radiance
// is the radiance of light coming from -Normal direction, which is what
// we use in the PBR shader to sample irradiance.
vec3 N = normalize(WorldPos);
vec3 irradiance = vec3(0.0);
// tangent space calculation from origin point
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = normalize(cross(up, N));
up = normalize(cross(N, right));
float sampleDelta = 0.025;
float nrSamples = 0.0;
for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
{
for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
{
// spherical to cartesian (in tangent space)
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
// tangent space to world
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;
irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);
nrSamples++;
}
}
irradiance = PI * irradiance * (1.0 / float(nrSamples));
FragColor = vec4(irradiance, 1.0);
}

View File

@ -1258,7 +1258,7 @@ void main()
imageStore(gBaseColor, pixelLocation, vec4(color.rgb,1)); imageStore(gBaseColor, pixelLocation, vec4(color.rgb,1));
imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1)); imageStore(gMetallicRoughness, pixelLocation, vec4(metallicRoughness, 0, 1));
//return; return;
if (/*color.a!=-1&&*/debugBVH==vec3(0)) if (/*color.a!=-1&&*/debugBVH==vec3(0))
{ {
//imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1)); //imageStore(gBaseColor, pixelLocation, vec4(vec3(1, 1, 0),1));

View File

@ -3,11 +3,15 @@
layout (local_size_x = 8, local_size_y = 8) in; layout (local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 0) uniform image2D gBaseColor; layout(rgba8, binding = 0) uniform image2D gBaseColor;
//uniform sampler2D gBaseColor;
uniform sampler2D gNormal; uniform sampler2D gNormal;
uniform sampler2D gPosition; uniform sampler2D gPosition;
uniform sampler2D gMetallicRoughness; uniform sampler2D gMetallicRoughness;
uniform sampler2DArray gShadowMap; uniform sampler2DArray gShadowMap;
uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform sampler2D brdfLUT;
layout(rgba16f, binding = 1) uniform image2D gDirectLight; layout(rgba16f, binding = 1) uniform image2D gDirectLight;
layout (std140, binding = 0) uniform LightSpaceMatrices layout (std140, binding = 0) uniform LightSpaceMatrices
{ {
@ -66,6 +70,34 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0)
{ {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
} }
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
{
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 ambientLighting(vec3 N, vec3 V, vec3 F0, vec3 albedo,float metallic, float roughness, float ao)
{
// ambient lighting (we now use IBL as the ambient term)
vec3 F = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness);
vec3 kS = F;
vec3 kD = 1.0 - kS;
kD *= 1.0 - metallic;
vec3 irradiance = texture(irradianceMap, N).rgb;
vec3 diffuse = irradiance * albedo;
// 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.
const float MAX_REFLECTION_LOD = 4.0;
vec3 R = reflect(-V, N);
vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb;
vec2 brdf = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg;
vec3 specular = prefilteredColor * (F * brdf.x + brdf.y);
return (kD * diffuse + specular) * ao;
}
const int pcfRadius = 3; const int pcfRadius = 3;
float getShadowFromLayer(vec3 fragPosWorldSpace, vec3 normal,int layer) float getShadowFromLayer(vec3 fragPosWorldSpace, vec3 normal,int layer)
{ {
@ -133,6 +165,8 @@ float ShadowCalculation(vec3 fragPosWorldSpace, vec3 normal, out int layer)
return shadow; return shadow;
} }
vec4 encodeRGBM(vec3 color) vec4 encodeRGBM(vec3 color)
{ {
if(dot(color,color)==0) if(dot(color,color)==0)
@ -197,9 +231,11 @@ void main()
float NdotL = max(dot(normal, L), 0.0); float NdotL = max(dot(normal, L), 0.0);
vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL; vec3 Lo = (kD * albedo / PI + specular) * radiance * NdotL;
vec3 ambient = ambientLighting(normal, V, F0, albedo, metallic, roughness, 1);
int debugLayer; int debugLayer;
float shadow = ShadowCalculation(worldPos, normal, debugLayer); float shadow = ShadowCalculation(worldPos, normal, debugLayer);
vec3 color = (1-shadow)*Lo + albedo*0.03; vec3 color = (1-shadow)*Lo + ambient;
imageStore(gBaseColor, pixelLocation, encodeRGBM(color)); imageStore(gBaseColor, pixelLocation, encodeRGBM(color));
} }

View File

@ -3,14 +3,30 @@ out vec4 FragColor;
in vec3 WorldPos; in vec3 WorldPos;
uniform samplerCube environmentMap; uniform samplerCube environmentMap;
uniform float exposure = 1;
vec3 ACESToneMapping(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 (color * (A * color + B)) / (color * (C * color + D) + E);
}
void main() void main()
{ {
vec3 envColor = texture(environmentMap, WorldPos).rgb; vec3 color = texture(environmentMap, WorldPos).rgb;
// HDR tonemap and gamma correct // HDR tonemap
envColor = envColor / (envColor + vec3(1.0)); //envColor = envColor / (envColor + vec3(1.0));
envColor = pow(envColor, vec3(1.0/2.2));
FragColor = vec4(envColor, 1.0); color *= exposure;
color = ACESToneMapping(color);
// gamma correct
color = pow(color, vec3(1.0/2.2));
FragColor = vec4(color, 1.0);
} }

View File

@ -0,0 +1,324 @@
#include "IblUtils.h"
#include <QOpenGLShaderProgram>
#include <QDebug>
#include <array>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
void Renderer::IblUtils::renderCube(QOpenGLFunctions_4_5_Core* glFunc)
{
static GLuint cubeVAO = 0, cubeVBO = 0;
// initialize (if necessary)
if (cubeVAO == 0)
{
float vertices[] = {
// back face
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left
// front face
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
// left face
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
// right face
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left
// bottom face
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
// top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left
};
glFunc->glGenVertexArrays(1, &cubeVAO);
glFunc->glGenBuffers(1, &cubeVBO);
// fill buffer
glFunc->glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glFunc->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// link vertex attributes
glFunc->glBindVertexArray(cubeVAO);
glFunc->glEnableVertexAttribArray(0);
glFunc->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glFunc->glEnableVertexAttribArray(1);
glFunc->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glFunc->glEnableVertexAttribArray(2);
glFunc->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glFunc->glBindBuffer(GL_ARRAY_BUFFER, 0);
glFunc->glBindVertexArray(0);
}
// render Cube
glFunc->glBindVertexArray(cubeVAO);
glFunc->glDrawArrays(GL_TRIANGLES, 0, 36);
glFunc->glBindVertexArray(0);
}
std::tuple<GLuint, GLuint, GLuint, GLuint> Renderer::IblUtils::precomputeCubemaps(QOpenGLFunctions_4_5_Core* glFunc)
{
// pbr: setup framebuffer
// ----------------------
unsigned int captureFBO;
unsigned int captureRBO;
glFunc->glGenFramebuffers(1, &captureFBO);
glFunc->glGenRenderbuffers(1, &captureRBO);
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glFunc->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
// pbr: set up projection and view matrices for capturing data onto the 6 cubemap face directions
// ----------------------------------------------------------------------------------------------
QMatrix4x4 captureProjection;
captureProjection.perspective(90.0f, 1.0f, 0.1f, 10.0f);
std::array<QMatrix4x4, 6> captureViews;
captureViews[0].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0f, 0.0f, 0.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[1].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0f, 0.0f, 0.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[2].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 1.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f));
captureViews[3].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, -1.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f));
captureViews[4].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[5].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f), QVector3D(0.0f, -1.0f, 0.0f));
GLuint envCubemap = generateCubemap(glFunc, captureFBO, captureRBO, captureProjection, captureViews);
GLuint irradianceMap = generateIrradianceMap(glFunc, captureFBO, captureRBO, captureProjection, captureViews, envCubemap);
GLuint prefilterMap = generatePrefilterMap(glFunc, captureFBO, captureRBO, captureProjection, captureViews, envCubemap);
GLuint brdfLut = gererateBrdfLut(glFunc);
return { envCubemap, irradianceMap, prefilterMap, brdfLut };
}
GLuint Renderer::IblUtils::generateCubemap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews)
{
// pbr: load the HDR environment map
// ---------------------------------
stbi_set_flip_vertically_on_load(true);
int width, height, nrComponents;
float* data = stbi_loadf("HDRI/clarens_midday_4k.hdr", &width, &height, &nrComponents, 0);
unsigned int hdrTexture = 0;
if (data)
{
glGenTextures(1, &hdrTexture);
glBindTexture(GL_TEXTURE_2D, hdrTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
qDebug() << "Failed to load HDR image.";
// pbr: setup cubemap to render to and attach to framebuffer
// ---------------------------------------------------------
unsigned int envCubemap;
glFunc->glGenTextures(1, &envCubemap);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);
for (unsigned int i = 0; i < 6; ++i)
{
glFunc->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, cubemapSize, cubemapSize, 0, GL_RGB, GL_FLOAT, nullptr);
}
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// pbr: convert HDR equirectangular environment map to cubemap equivalent
// ----------------------------------------------------------------------
QOpenGLShaderProgram shader;
if (!shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Shaders/cubemap.vert"))
qDebug() << "ERROR:" << shader.log();
if (!shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/Shaders/cubemap.frag"))
qDebug() << "ERROR:" << shader.log();
if (!shader.link())
qDebug() << "ERROR:" << shader.log();
shader.bind();
shader.setUniformValue("equirectangularMap", 0);
shader.setUniformValue("projection", captureProjection);
glFunc->glActiveTexture(GL_TEXTURE0);
glFunc->glBindTexture(GL_TEXTURE_2D, hdrTexture);
glFunc->glViewport(0, 0, cubemapSize, cubemapSize);
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (unsigned int i = 0; i < 6; ++i)
{
shader.setUniformValue("view", captureViews[i]);
glFunc->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, 0);
glFunc->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderCube(glFunc);
}
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, 0);
return envCubemap;
}
GLuint Renderer::IblUtils::generateIrradianceMap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews, GLuint envCubemap)
{
constexpr int irradianceMapSize = 32;
// pbr: create an irradiance cubemap, and re-scale capture FBO to irradiance scale.
// --------------------------------------------------------------------------------
unsigned int irradianceMap;
glFunc->glGenTextures(1, &irradianceMap);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceMap);
for (unsigned int i = 0; i < 6; ++i)
{
glFunc->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, irradianceMapSize, irradianceMapSize, 0, GL_RGB, GL_FLOAT, nullptr);
}
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glFunc->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glFunc->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, irradianceMapSize, irradianceMapSize);
// pbr: solve diffuse integral by convolution to create an irradiance (cube)map.
// -----------------------------------------------------------------------------
QOpenGLShaderProgram irradianceShader;
if (!irradianceShader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Shaders/cubemap.vert"))
qDebug() << "ERROR:" << irradianceShader.log();
if (!irradianceShader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/Shaders/irradiance_convolution.frag"))
qDebug() << "ERROR:" << irradianceShader.log();
if (!irradianceShader.link())
qDebug() << "ERROR:" << irradianceShader.log();
irradianceShader.bind();
irradianceShader.setUniformValue("environmentMap", 0);
irradianceShader.setUniformValue("projection", captureProjection);
glFunc->glActiveTexture(GL_TEXTURE0);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);
glFunc->glViewport(0, 0, irradianceMapSize, irradianceMapSize); // don't forget to configure the viewport to the capture dimensions.
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (unsigned int i = 0; i < 6; ++i)
{
irradianceShader.setUniformValue("view", captureViews[i]);
glFunc->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceMap, 0);
glFunc->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderCube(glFunc);
}
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, 0);
return irradianceMap;
}
GLuint Renderer::IblUtils::generatePrefilterMap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews, GLuint envCubemap)
{
constexpr int prefilterMapSize = 128;
// pbr: create a pre-filter cubemap, and re-scale capture FBO to pre-filter scale.
// --------------------------------------------------------------------------------
unsigned int prefilterMap;
glFunc->glGenTextures(1, &prefilterMap);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterMap);
for (unsigned int i = 0; i < 6; ++i)
{
glFunc->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, prefilterMapSize, prefilterMapSize, 0, GL_RGB, GL_FLOAT, nullptr);
}
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // be sure to set minification filter to mip_linear
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// generate mipmaps for the cubemap so OpenGL automatically allocates the required memory.
glFunc->glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
// pbr: run a quasi monte-carlo simulation on the environment lighting to create a prefilter (cube)map.
// ----------------------------------------------------------------------------------------------------
QOpenGLShaderProgram prefilterShader;
if (!prefilterShader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Shaders/cubemap.vert"))
qDebug() << "ERROR:" << prefilterShader.log();
if (!prefilterShader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/Shaders/cubemap_prefilter.frag"))
qDebug() << "ERROR:" << prefilterShader.log();
if (!prefilterShader.link())
qDebug() << "ERROR:" << prefilterShader.log();
prefilterShader.bind();
prefilterShader.setUniformValue("environmentMap", 0);
prefilterShader.setUniformValue("projection", captureProjection);
glFunc->glActiveTexture(GL_TEXTURE0);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
unsigned int maxMipLevels = 5;
for (unsigned int mip = 0; mip < maxMipLevels; ++mip)
{
// reisze framebuffer according to mip-level size.
unsigned int mipWidth = static_cast<unsigned int>(prefilterMapSize * std::pow(0.5, mip));
unsigned int mipHeight = static_cast<unsigned int>(prefilterMapSize * std::pow(0.5, mip));
glFunc->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glFunc->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
glFunc->glViewport(0, 0, mipWidth, mipHeight);
float roughness = (float)mip / (float)(maxMipLevels - 1);
prefilterShader.setUniformValue("roughness", roughness);
for (unsigned int i = 0; i < 6; ++i)
{
prefilterShader.setUniformValue("view", captureViews[i]);
glFunc->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, prefilterMap, mip);
glFunc->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderCube(glFunc);
}
}
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, 0);
return prefilterMap;
}
GLuint Renderer::IblUtils::gererateBrdfLut(QOpenGLFunctions_4_5_Core* glFunc)
{
constexpr int lutSize = 512;
// pbr: generate a 2D LUT from the BRDF equations used.
// ----------------------------------------------------
unsigned int brdfLUTTexture;
glFunc->glGenTextures(1, &brdfLUTTexture);
// pre-allocate enough memory for the LUT texture.
glFunc->glBindTexture(GL_TEXTURE_2D, brdfLUTTexture);
glFunc->glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, lutSize, lutSize, 0, GL_RG, GL_FLOAT, 0);
// be sure to set wrapping mode to GL_CLAMP_TO_EDGE
glFunc->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFunc->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
QOpenGLShaderProgram brdfShader;
if (!brdfShader.addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/brdf_lut.comp"))
qDebug() << "ERROR:" << brdfShader.log();
if (!brdfShader.link())
qDebug() << "ERROR:" << brdfShader.log();
brdfShader.bind();
glFunc->glBindImageTexture(0, brdfLUTTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RG16F);
glFunc->glDispatchCompute(ceil(lutSize / 8.), ceil(lutSize / 8.), 1);
brdfShader.release();
return brdfLUTTexture;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <QOpenGLFunctions_4_5_Core>
namespace Renderer
{
class IblUtils
{
public:
static constexpr int cubemapSize = 1024;
static void renderCube(QOpenGLFunctions_4_5_Core* glFunc);
/**
* @brief
* @return GLuint envCubemap
* @return GLuint irradianceMap
*/
static std::tuple<GLuint, GLuint, GLuint, GLuint> precomputeCubemaps(QOpenGLFunctions_4_5_Core* glFunc);
private:
static GLuint generateCubemap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews);
static GLuint generateIrradianceMap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews, GLuint envCubemap);
static GLuint generatePrefilterMap(QOpenGLFunctions_4_5_Core* glFunc, GLuint captureFBO, GLuint captureRBO, const QMatrix4x4& captureProjection, const std::array<QMatrix4x4, 6>& captureViews, GLuint envCubemap);
static GLuint gererateBrdfLut(QOpenGLFunctions_4_5_Core* glFunc);
};
}

View File

@ -7,7 +7,7 @@ using namespace Renderer;
std::vector<BaseStyle> Renderer::ElementStyleFillDemo::toBaseStyles() const std::vector<BaseStyle> Renderer::ElementStyleFillDemo::toBaseStyles() const
{ {
return { BaseStyle(std::make_shared<TransformStyle>(), return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleFill>(std::make_shared<FillPlain>(QColor(0, 255, 0), 0, 0.8))) }; std::make_shared<MaterialStyleFill>(std::make_shared<FillPlain>(QColor(202, 148, 44), 1, 0.18))) };
} }
Renderer::ElementStyleStrokeDemo::ElementStyleStrokeDemo(float width) Renderer::ElementStyleStrokeDemo::ElementStyleStrokeDemo(float width)

View File

@ -1,7 +1,7 @@
#include "PaintingHelper.h" #include "PaintingHelper.h"
using namespace Renderer; using namespace Renderer;
PaintingHelper::PaintingHelper(QOpenGLFunctions_4_5_Compatibility* glFunc) :glFunc(glFunc) PaintingHelper::PaintingHelper(QOpenGLFunctions_4_5_Core* glFunc) :glFunc(glFunc)
{ {
} }

View File

@ -3,7 +3,7 @@
#include <vector> #include <vector>
#include <QDebug> #include <QDebug>
#include <QVector4D> #include <QVector4D>
#include <QOpenGLFunctions_4_5_Compatibility> #include <QOpenGLFunctions_4_5_Core>
#include "BvhTree.h" #include "BvhTree.h"
#include "Painting.h" #include "Painting.h"
namespace Renderer namespace Renderer
@ -11,7 +11,7 @@ namespace Renderer
class PaintingHelper class PaintingHelper
{ {
private: private:
QOpenGLFunctions_4_5_Compatibility* glFunc; QOpenGLFunctions_4_5_Core* glFunc;
GLuint paintingOffsetsSSBO, bvhSSBO, bvhBoundSSBO, elementOffsetSSBO, elementIndexSSBO, elementDataSSBO; GLuint paintingOffsetsSSBO, bvhSSBO, bvhBoundSSBO, elementOffsetSSBO, elementIndexSSBO, elementDataSSBO;
std::vector<GLuint> paintingOffsets; std::vector<GLuint> paintingOffsets;
std::vector<GLuint> bvhChildren; std::vector<GLuint> bvhChildren;
@ -22,7 +22,7 @@ namespace Renderer
int paintingCount = 0; int paintingCount = 0;
public: public:
PaintingHelper(QOpenGLFunctions_4_5_Compatibility* glFunc); PaintingHelper(QOpenGLFunctions_4_5_Core* glFunc);
int addPainting(Painting painting); int addPainting(Painting painting);
int addPainting(std::vector<GLuint> bvhChildren, std::vector<QVector4D> bvhBound, int addPainting(std::vector<GLuint> bvhChildren, std::vector<QVector4D> bvhBound,
std::vector<glm::uvec4> elementOffset, std::vector<GLuint> elementIndex, std::vector<GLfloat> elementData); std::vector<glm::uvec4> elementOffset, std::vector<GLuint> elementIndex, std::vector<GLfloat> elementData);

View File

@ -7,12 +7,11 @@
#include <QGuiApplication> #include <QGuiApplication>
#include <random> #include <random>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#define STB_IMAGE_IMPLEMENTATION #include "IblUtils.h"
#include <stb_image.h>
using namespace Renderer; using namespace Renderer;
//QVector3D lightPositions[] = { 2000 * QVector3D(0.2, 4, 1).normalized(), QVector3D(100,100,100) ,QVector3D(-100,100,100) ,QVector3D(100,100,-100) };
QVector3D lightColors[] = { 20 * QVector3D(0.7529,0.7450,0.6784).normalized(), QVector3D(0,0,0) ,QVector3D(0,0,0) ,QVector3D(0,0,0) }; QVector3D mainLightRadiance = 40 * QVector3D(0.7529, 0.7450, 0.6784).normalized();
static float sunPitch = 105, sunYaw = 80; static float sunPitch = 105, sunYaw = 80;
static int sunSpeed = 10; static int sunSpeed = 10;
@ -92,181 +91,24 @@ void RendererGLWidget::setMainLightYaw(float yaw)
//qDebug() << "yaw" << yaw; //qDebug() << "yaw" << yaw;
sunYaw = yaw; sunYaw = yaw;
} }
void Renderer::RendererGLWidget::setExposure(float exposure)
{
this->exposure = exposure;
}
QOpenGLTexture randomMap(QOpenGLTexture::Target2D); QOpenGLTexture randomMap(QOpenGLTexture::Target2D);
void renderCube(QOpenGLFunctions_4_5_Compatibility* glFunc)
{
static GLuint cubeVAO = 0, cubeVBO = 0;
// initialize (if necessary)
if (cubeVAO == 0)
{
float vertices[] = {
// back face
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // top-right
-1.0f, -1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
-1.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, // top-left
// front face
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // top-right
-1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // top-left
-1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom-left
// left face
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
-1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-left
-1.0f, -1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-right
// right face
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left
// bottom face
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom-left
-1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom-right
-1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // top-right
// top face
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
1.0f, 1.0f , 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top-right
1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom-right
-1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // top-left
-1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f // bottom-left
};
glFunc->glGenVertexArrays(1, &cubeVAO);
glFunc->glGenBuffers(1, &cubeVBO);
// fill buffer
glFunc->glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glFunc->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// link vertex attributes
glFunc->glBindVertexArray(cubeVAO);
glFunc->glEnableVertexAttribArray(0);
glFunc->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glFunc->glEnableVertexAttribArray(1);
glFunc->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glFunc->glEnableVertexAttribArray(2);
glFunc->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glFunc->glBindBuffer(GL_ARRAY_BUFFER, 0);
glFunc->glBindVertexArray(0);
}
// render Cube
glFunc->glBindVertexArray(cubeVAO);
glFunc->glDrawArrays(GL_TRIANGLES, 0, 36);
glFunc->glBindVertexArray(0);
}
GLuint generateCubemap(QOpenGLFunctions_4_5_Compatibility* glFunc)
{
QOpenGLShaderProgram shader;
if (!shader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Shaders/cubemap.vert"))
qDebug() << "ERROR:" << shader.log();
if (!shader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/Shaders/cubemap.frag"))
qDebug() << "ERROR:" << shader.log();
if (!shader.link())
qDebug() << "ERROR:" << shader.log();
// pbr: setup framebuffer
// ----------------------
unsigned int captureFBO;
unsigned int captureRBO;
glFunc->glGenFramebuffers(1, &captureFBO);
glFunc->glGenRenderbuffers(1, &captureRBO);
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
glFunc->glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);
glFunc->glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 1024, 1024);
glFunc->glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);
// pbr: load the HDR environment map
// ---------------------------------
stbi_set_flip_vertically_on_load(true);
int width, height, nrComponents;
float* data = stbi_loadf("HDRI/clarens_midday_4k.hdr", &width, &height, &nrComponents, 0);
unsigned int hdrTexture;
if (data)
{
glGenTextures(1, &hdrTexture);
glBindTexture(GL_TEXTURE_2D, hdrTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
std::cout << "Failed to load HDR image." << std::endl;
// pbr: setup cubemap to render to and attach to framebuffer
// ---------------------------------------------------------
unsigned int envCubemap;
glFunc->glGenTextures(1, &envCubemap);
glFunc->glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);
for (unsigned int i = 0; i < 6; ++i)
{
glFunc->glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr);
}
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glFunc->glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// pbr: set up projection and view matrices for capturing data onto the 6 cubemap face directions
// ----------------------------------------------------------------------------------------------
QMatrix4x4 captureProjection;
captureProjection.perspective(90.0f, 1.0f, 0.1f, 10.0f);
QMatrix4x4 captureViews[6];
captureViews[0].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D( 1.0f, 0.0f, 0.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[1].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0f, 0.0f, 0.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[2].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D( 0.0f, 1.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f));
captureViews[3].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D( 0.0f, -1.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f));
captureViews[4].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D( 0.0f, 0.0f, 1.0f), QVector3D(0.0f, -1.0f, 0.0f));
captureViews[5].lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D( 0.0f, 0.0f, -1.0f), QVector3D(0.0f, -1.0f, 0.0f));
// pbr: convert HDR equirectangular environment map to cubemap equivalent
// ----------------------------------------------------------------------
shader.bind();
shader.setUniformValue("equirectangularMap", 0);
shader.setUniformValue("projection", captureProjection);
glFunc->glActiveTexture(GL_TEXTURE0);
glFunc->glBindTexture(GL_TEXTURE_2D, hdrTexture);
glFunc->glViewport(0, 0, 512, 512); // don't forget to configure the viewport to the capture dimensions.
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);
for (unsigned int i = 0; i < 6; ++i)
{
shader.setUniformValue("view", captureViews[i]);
glFunc->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, 0);
glFunc->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderCube(glFunc);
}
glFunc->glBindFramebuffer(GL_FRAMEBUFFER, 0);
return envCubemap;
}
void RendererGLWidget::initializeGL() void RendererGLWidget::initializeGL()
{ {
initializeOpenGLFunctions(); initializeOpenGLFunctions();
QOpenGLFunctions_4_5_Core* glFunc = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Core>();
qDebug() << "GL_VERSION" << (char*)glGetString(GL_VERSION); qDebug() << "GL_VERSION" << (char*)glGetString(GL_VERSION);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
glClearColor(0, 0, 0, 1); glClearColor(0, 0, 0, 1);
shadowProgramPtr = new QOpenGLShaderProgram; shadowProgramPtr = new QOpenGLShaderProgram;
@ -360,6 +202,9 @@ void RendererGLWidget::initializeGL()
shadowMappingProgramPtr->setUniformValue("gPosition", 2); shadowMappingProgramPtr->setUniformValue("gPosition", 2);
shadowMappingProgramPtr->setUniformValue("gMetallicRoughness", 3); shadowMappingProgramPtr->setUniformValue("gMetallicRoughness", 3);
shadowMappingProgramPtr->setUniformValue("gShadowMap", 4); shadowMappingProgramPtr->setUniformValue("gShadowMap", 4);
shadowMappingProgramPtr->setUniformValue("irradianceMap", 5);
shadowMappingProgramPtr->setUniformValue("prefilterMap", 6);
shadowMappingProgramPtr->setUniformValue("brdfLUT", 7);
shadowMappingProgramPtr->release(); shadowMappingProgramPtr->release();
ssgiProgramPtr->bind(); ssgiProgramPtr->bind();
@ -372,46 +217,15 @@ void RendererGLWidget::initializeGL()
finalProgramPtr->bind(); finalProgramPtr->bind();
finalProgramPtr->setUniformValue("gBaseColor", 0); finalProgramPtr->setUniformValue("gBaseColor", 0);
finalProgramPtr->setUniformValue("gDirectLight", 1);
finalProgramPtr->setUniformValue("gIndirectLight", 2);
/*finalProgramPtr->setUniformValue("gNormal", 1);
finalProgramPtr->setUniformValue("gPosition", 2);
finalProgramPtr->setUniformValue("gMetallicRoughness", 3);
finalProgramPtr->setUniformValue("gDepth", 4);
finalProgramPtr->setUniformValue("gDirectLight", 5);*/
//std::vector<QVector3D> rsmSampleCoordsAndWeights;
//std::default_random_engine e;
//std::uniform_real_distribution<float> u(0, 1);
//int rsmSampleNum = 50;
//finalProgramPtr->setUniformValue("rsmSampleNum", rsmSampleNum);
//for (int i = 0; i < rsmSampleNum; ++i)
//{
// float xi1 = u(e);
// float xi2 = u(e);
// rsmSampleCoordsAndWeights.push_back(QVector3D(xi1 * sin(2 * M_PI * xi2), xi1 * cos(2 * M_PI * xi2), xi1 * xi1));
//}
////QOpenGLTexture randomMap(QOpenGLTexture::Target2D);
//randomMap.create();
//randomMap.setSize(rsmSampleCoordsAndWeights.size());
//randomMap.setFormat(QOpenGLTexture::RGB8_UNorm);
//randomMap.allocateStorage();
//randomMap.setData(QOpenGLTexture::RGB, QOpenGLTexture::Float32, rsmSampleCoordsAndWeights.data());
//randomMap.setMinMagFilters(QOpenGLTexture::Nearest, QOpenGLTexture::Nearest);
finalProgramPtr->release(); finalProgramPtr->release();
paintingHelper = new PaintingHelper(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>()); paintingHelper = new PaintingHelper(glFunc);
model = new Model(context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper); model = new Model(context(), modelProgramPtr, paintingProgramPtr, shadowProgramPtr, paintingHelper);
skyBoxProgramPtr->bind(); skyBoxProgramPtr->bind();
skyBoxProgramPtr->setUniformValue("environmentMap", 0); skyBoxProgramPtr->setUniformValue("environmentMap", 0);
skyBoxProgramPtr->release(); skyBoxProgramPtr->release();
skyCubemap = generateCubemap(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>()); std::tie(skyCubemap, irradianceMap, prefilterMap, brdfLUTTexture) = IblUtils::precomputeCubemaps(glFunc);
quadVAO.create(); quadVAO.create();
QOpenGLVertexArrayObject::Binder vaoBinder(&quadVAO); QOpenGLVertexArrayObject::Binder vaoBinder(&quadVAO);
@ -442,6 +256,8 @@ void RendererGLWidget::initializeGL()
void RendererGLWidget::paintGL() void RendererGLWidget::paintGL()
{ {
QOpenGLFunctions_4_5_Core* glFunc = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Core>();
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
light.lightDirection.setX(cos(qDegreesToRadians(sunPitch)) * cos(qDegreesToRadians(sunYaw))); light.lightDirection.setX(cos(qDegreesToRadians(sunPitch)) * cos(qDegreesToRadians(sunYaw)));
@ -532,7 +348,7 @@ void RendererGLWidget::paintGL()
shadowMappingProgramPtr->setUniformValue("shadowBlendRatio", light.blendRatio); shadowMappingProgramPtr->setUniformValue("shadowBlendRatio", light.blendRatio);
shadowMappingProgramPtr->setUniformValue("camPos", camera.Position); shadowMappingProgramPtr->setUniformValue("camPos", camera.Position);
shadowMappingProgramPtr->setUniformValue("mainLightDirection", light.lightDirection); shadowMappingProgramPtr->setUniformValue("mainLightDirection", light.lightDirection);
shadowMappingProgramPtr->setUniformValue("mainLightRadiance", lightColors[0]); shadowMappingProgramPtr->setUniformValue("mainLightRadiance", mainLightRadiance);
/*glActiveTexture(GL_TEXTURE0); /*glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gbuffers[0]);*/ glBindTexture(GL_TEXTURE_2D, gbuffers[0]);*/
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
@ -543,6 +359,12 @@ void RendererGLWidget::paintGL()
glBindTexture(GL_TEXTURE_2D, gbuffers[3]); glBindTexture(GL_TEXTURE_2D, gbuffers[3]);
glActiveTexture(GL_TEXTURE4); glActiveTexture(GL_TEXTURE4);
glBindTexture(GL_TEXTURE_2D_ARRAY, shadowGbuffer); glBindTexture(GL_TEXTURE_2D_ARRAY, shadowGbuffer);
glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceMap);
glActiveTexture(GL_TEXTURE6);
glBindTexture(GL_TEXTURE_CUBE_MAP, prefilterMap);
glActiveTexture(GL_TEXTURE7);
glBindTexture(GL_TEXTURE_2D, brdfLUTTexture);
glBindImageTexture(0, gbuffers[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8); glBindImageTexture(0, gbuffers[0], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
glBindImageTexture(1, gbuffers[8], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA16F); glBindImageTexture(1, gbuffers[8], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA16F);
glDispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1); glDispatchCompute(ceil(frameWidth / 8.), ceil(frameHeight / 8.), 1);
@ -576,25 +398,9 @@ void RendererGLWidget::paintGL()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//QOpenGLVertexArrayObject::Binder vaoBinder(&quadVAO); //QOpenGLVertexArrayObject::Binder vaoBinder(&quadVAO);
finalProgramPtr->bind(); finalProgramPtr->bind();
/*finalProgramPtr->setUniformValue("camPos", camera.Position); finalProgramPtr->setUniformValue("exposure", exposure);
finalProgramPtr->setUniformValue("mainLightDirection", light.lightDirection);
finalProgramPtr->setUniformValue("mainLightRadiance", lightColors[0]);*/
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gbuffers[0]); glBindTexture(GL_TEXTURE_2D, gbuffers[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gbuffers[8]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gbuffers[9]);
//glActiveTexture(GL_TEXTURE1);
//glBindTexture(GL_TEXTURE_2D, gbuffers[1]);
//glActiveTexture(GL_TEXTURE2);
//glBindTexture(GL_TEXTURE_2D, gbuffers[2]);
//glActiveTexture(GL_TEXTURE3);
//glBindTexture(GL_TEXTURE_2D, gbuffers[3]);
//glActiveTexture(GL_TEXTURE4);
//glBindTexture(GL_TEXTURE_2D, gbuffers[7]);
//glActiveTexture(GL_TEXTURE5);
//glBindTexture(GL_TEXTURE_2D, gbuffers[8]);
quadVAO.bind(); quadVAO.bind();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
quadVAO.release(); quadVAO.release();
@ -604,9 +410,10 @@ void RendererGLWidget::paintGL()
glDisable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
skyBoxProgramPtr->setUniformValue("view", view); skyBoxProgramPtr->setUniformValue("view", view);
skyBoxProgramPtr->setUniformValue("projection", projection); skyBoxProgramPtr->setUniformValue("projection", projection);
skyBoxProgramPtr->setUniformValue("exposure", exposure);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyCubemap); glBindTexture(GL_TEXTURE_CUBE_MAP, skyCubemap);
renderCube(QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_4_5_Compatibility>()); IblUtils::renderCube(glFunc);
skyBoxProgramPtr->release(); skyBoxProgramPtr->release();
GLuint paintingCompDuration; GLuint paintingCompDuration;

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Compatibility> #include <QOpenGLFunctions_4_5_Core>
#include <QOpenGLShaderProgram> #include <QOpenGLShaderProgram>
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject> #include <QOpenGLVertexArrayObject>
@ -13,7 +13,7 @@
#include "Painting/PaintingHelper.h" #include "Painting/PaintingHelper.h"
namespace Renderer namespace Renderer
{ {
class RendererGLWidget : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Compatibility class RendererGLWidget : public QOpenGLWidget, protected QOpenGLFunctions_4_5_Core
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -26,6 +26,7 @@ namespace Renderer
void setModel(); void setModel();
void setMainLightPitch(float pitch); void setMainLightPitch(float pitch);
void setMainLightYaw(float yaw); void setMainLightYaw(float yaw);
void setExposure(float exposure);
protected: protected:
void initializeGL() override; void initializeGL() override;
void paintGL() override; void paintGL() override;
@ -47,6 +48,7 @@ namespace Renderer
clock_t lastFrame; clock_t lastFrame;
float deltaTime; float deltaTime;
int shadowMapResolution; int shadowMapResolution;
float exposure = 0.8;
QOpenGLShaderProgram* shadowProgramPtr = nullptr; QOpenGLShaderProgram* shadowProgramPtr = nullptr;
QOpenGLShaderProgram* modelProgramPtr = nullptr; QOpenGLShaderProgram* modelProgramPtr = nullptr;
@ -64,6 +66,9 @@ namespace Renderer
GLuint shadowGbuffer; GLuint shadowGbuffer;
GLuint lightSpaceMatricesUBO; GLuint lightSpaceMatricesUBO;
GLuint skyCubemap; GLuint skyCubemap;
GLuint irradianceMap;
GLuint prefilterMap;
GLuint brdfLUTTexture;
QOpenGLBuffer quadVBO; QOpenGLBuffer quadVBO;
QOpenGLVertexArrayObject quadVAO; QOpenGLVertexArrayObject quadVAO;
Model* model = nullptr; Model* model = nullptr;

View File

@ -9,8 +9,13 @@ RendererWidget::RendererWidget(QWidget *parent)
ui.openGLWidget, &RendererGLWidget::setMainLightPitch); ui.openGLWidget, &RendererGLWidget::setMainLightPitch);
QObject::connect(ui.horizontalSlider_2, &QSlider::valueChanged, QObject::connect(ui.horizontalSlider_2, &QSlider::valueChanged,
ui.openGLWidget, &RendererGLWidget::setMainLightYaw); ui.openGLWidget, &RendererGLWidget::setMainLightYaw);
QObject::connect(ui.exposureSlider, &QSlider::valueChanged, [&](int value) {
ui.openGLWidget->setExposure(value/100.f);
});
ui.horizontalSlider->setValue(105); ui.horizontalSlider->setValue(105);
ui.horizontalSlider_2->setValue(80); ui.horizontalSlider_2->setValue(80);
ui.exposureSlider->setValue(60);
QObject::connect(ui.openButton, &QPushButton::clicked, QObject::connect(ui.openButton, &QPushButton::clicked,
ui.openGLWidget, &RendererGLWidget::setModel); ui.openGLWidget, &RendererGLWidget::setModel);
} }

View File

@ -19,7 +19,7 @@
### 场景渲染Renderer ### 场景渲染Renderer
采用 PBR (金属度-粗糙度) 材质模型,场景中存在一个方向光,可拖动滑动条调整光源方向观察材质效果 采用 PBR (金属度-粗糙度) 材质模型
已实现由图元数据建立完整彩绘编码 已实现由图元数据建立完整彩绘编码