Commit cfce9b4c authored by Philip Trettner's avatar Philip Trettner

Working on material system

parent 1ed66566
......@@ -20,6 +20,10 @@ target_link_libraries(glow-extras PUBLIC glow-extras-geometry)
add_subdirectory(pipeline)
target_link_libraries(glow-extras PUBLIC glow-extras-pipeline)
# Material shading library
add_subdirectory(material)
target_link_libraries(glow-extras PUBLIC glow-extras-material)
# Timing
# requires 'aion'
if (TARGET aion)
......
cmake_minimum_required(VERSION 3.0)
file(GLOB_RECURSE SOURCE_FILES "*.cc")
file(GLOB_RECURSE HEADER_FILES "*.hh")
file(GLOB_RECURSE SHADER_FILES "shader/*.*")
add_library(glow-extras-material ${GLOW_LINK_TYPE} ${SOURCE_FILES} ${HEADER_FILES} ${SHADER_FILES})
target_include_directories(glow-extras-material PUBLIC ./)
target_compile_options(glow-extras-material PRIVATE -Wall -Werror -std=c++11)
target_link_libraries(glow-extras-material PUBLIC glow glow-extras-camera)
target_link_libraries(glow-extras-material PRIVATE glow-extras-geometry)
# Material Shading Library
## General considerations
* Diffuse Roughness != Specular Roughness
* especially normal maps for diffuse and specular should be different
* Idea: use higher LoD for diffuse
* Diffuse gets 'left-over' light from Specular
* Specular Color is F0 term in Fresnel Term (at 0°)
* Normal Distribution Function (NDF) incorporates anisotropy
* Always use Smith Function for Geometry term
## Material Types
### Metals (conductors)
* Have no diffuse color (total absorption)
### Non-Metals (dielectrics, insulators)
* Have achromatic specular color
#pragma once
#include <glow/std140.hh>
namespace glow
{
namespace material
{
struct GGXMaterial
{
std140vec3 color;
std140float roughness;
std140float metallic;
};
}
}
#include "IBL.hh"
#include <glow/objects/Texture2D.hh>
#include <glow/objects/TextureCubeMap.hh>
#include <glow/objects/Program.hh>
glow::SharedTexture2D glow::material::IBL::createEnvLutGGX(int width, int height)
{
// only one mip-map level
auto tex = Texture2D::createStorageImmutable(width, height, GL_RG16F, 1);
// setup
{
auto t = tex->bind();
t.setMinFilter(GL_LINEAR);
t.setMagFilter(GL_LINEAR);
t.setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
}
// compute
const int localSize = 4;
auto shader = Program::createFromFile("glow-material/precalc-env-brdf-lut.csh");
{
auto s = shader->use();
s.setImage(0, tex);
s.compute((width - 1) / localSize + 1, (height - 1) / localSize + 1);
}
return tex;
}
glow::SharedTextureCubeMap glow::material::IBL::createEnvMapGGX(const glow::SharedTextureCubeMap &envMap, int size)
{
// TODO
return nullptr;
}
#pragma once
#include <glow/fwd.hh>
namespace glow
{
namespace material
{
/// Static helper class for pre-calculating IBL-related maps
///
/// Requires access to material shaders, e.g.:
/// DefaultShaderParser::addIncludePath("PATH/TO/glow-extras/material/shader");
///
class IBL
{
public:
/**
* @brief creates a 2D look-up table for the GGX environment BRDF
*
* Parametrization:
* x: roughness in [0,1]
* y: dot(N, V) in [0,1]
*
* Linear filtering, no mipmaps, clamp to edge
*/
static SharedTexture2D createEnvLutGGX(int width = 64, int height = 64);
/**
* @brief creates a pre-filtered environment map for use with GGX IBL
*/
static SharedTextureCubeMap createEnvMapGGX(SharedTextureCubeMap const& envMap, int size = 512);
private:
IBL() = delete;
};
}
}
// DO NOT MULTIPLY BY COS THETA
vec3 shadingSpecularGGX(vec3 N, vec3 V, vec3 L, float roughness, vec3 F0)
{
// see http://www.filmicworlds.com/2014/04/21/optimizing-ggx-shaders-with-dotlh/
vec3 H = normalize(V + L);
float dotLH = max(dot(L, H), 0.0);
float dotNH = max(dot(N, H), 0.0);
float dotNL = max(dot(N, L), 0.0);
float dotNV = max(dot(N, V), 0.0);
float alpha = roughness * roughness;
// D (GGX normal distribution)
float alphaSqr = alpha * alpha;
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
float D = alphaSqr / (denom * denom);
// no pi because BRDF -> lighting
// F (Fresnel term)
float F_a = 1.0;
float F_b = pow(1.0 - dotLH, 5); // manually?
vec3 F = mix(vec3(F_b), vec3(F_a), F0);
// G (remapped hotness, see Unreal Shading)
float k = (alpha + 2 * roughness + 1) / 8.0;
float G = dotNL / (mix(dotNL, 1, k) * mix(dotNV, 1, k));
// '* dotNV' - canceled by normalization
// orginal G:
/*
{
float k = alpha / 2.0;
float k2 = k * k;
float invK2 = 1.0 - k2;
float vis = 1 / (dotLH * dotLH * invK2 + k2);
vec3 FV = mix(vec3(F_b), vec3(F_a), F0) * vis;
vec3 specular = D * FV / 4.0f;
return specular * dotNL;
}
*/
// '/ dotLN' - canceled by lambert
// '/ dotNV' - canceled by G
return D * F * G / 4.0;
}
// specular and diffuse contribution of a single light direction
vec3 shadingGGX(vec3 N, vec3 V, vec3 L, vec3 color, float roughness, float metallic)
{
vec3 diffuse = color * (1 - metallic); // metals have no diffuse
vec3 specular = mix(vec3(0.04), color, metallic); // fixed spec for non-metals
float dotNL = max(dot(N, L), 0.0);
return diffuse * dotNL + shadingSpecularGGX(N, V, L, roughness, specular);
}
// see http://www.gamedev.net/topic/655431-ibl-problem-with-consistency-using-ggx-anisotropy/
#include "precalc.glsl"
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
vec2 IntegrateBRDF( float Roughness, float dotNV )
{
vec3 V = vec3( sqrt(1 - dotNV * dotNV), // sin
0.0,
dotNV ); // cos
float A = 0;
float B = 0;
vec3 N = vec3(0, 0, 1);
float k = Roughness / 2.0;
k = k * k;
const int samples = 1024;
for (int i = 0; i < samples; ++i)
{
vec2 Xi = Hammersley(i, samples);
vec3 H = ImportanceSampleGGX( Xi, Roughness, N );
vec3 L = 2 * dot(V, H) * H - V;
float dotNL = max(L.z, 0.0);
float dotNH = max(H.z, 0.0);
float dotVH = max(dot(V, H), 0.0);
if (dotNL > 0)
{
// original:
// float G = dotNL * dotNV / (mix(dotNV, 1, k) * mix(dotNL, 1, k));
// float G_Vis = G * dotVH / (dotNH * dotNV);
// slightly optimized
float G = dotNL / (mix(dotNV, 1, k) * mix(dotNL, 1, k));
float G_Vis = G * dotVH / dotNH;
float Fc = pow(1 - dotVH, 5);
A += (1 - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
return vec2(A, B) / float(samples);
}
/// COMPUTE SHADER
layout(local_size_x = 4, local_size_y = 4, local_size_z = 1) in;
uniform layout(rg16f, binding=0) writeonly image2DRect uLUT;
void main()
{
uint x = gl_GlobalInvocationID.x;
uint y = gl_GlobalInvocationID.y;
ivec2 s = imageSize(uLUT);
if (x >= s.x || y >= s.y)
return; // out of bounds
vec2 lut = IntegrateBRDF((float(x) + .5) / float(s.x), (float(y) + .5) / float(s.y));
imageStore(uLUT, ivec2(x, y), vec4(lut, 0, 0));
}
// see http://www.gamedev.net/topic/655431-ibl-problem-with-consistency-using-ggx-anisotropy/
#include "precalc.glsl"
uniform samplerCube uEnvMap;
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
vec3 PrefilterEnvMap( float Roughness, vec3 R )
{
vec3 N = R;
vec3 V = R;
vec3 color = vec3(0, 0, 0);
float totalWeight = 0;
const int samples = 1024;
for (int i = 0; i < samples; ++i)
{
vec2 Xi = Hammersley(i, samples);
vec3 H = ImportanceSampleGGX( Xi, Roughness, N );
vec3 L = 2 * dot(V, H) * H - V;
float dotNL = dot(N, L);
if (dotNL > 0)
{
color += textureLod(uEnvMap, L, 0);
totalWeight += dotNL;
}
}
return color / totalWeight;
}
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
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
}
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
vec2 Hammersley(uint i, uint N) {
return vec2(float(i)/float(N), radicalInverse_VdC(i));
}
// Image-Based Lighting
// http://www.unrealengine.com/files/downloads/2013SiggraphPresentationsNotes.pdf
vec3 ImportanceSampleGGX( vec2 Xi, float Roughness, vec3 N )
{
float PI = 3.1415926535897932384626433832795;
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
......@@ -12,4 +12,5 @@ void main()
color = clamp(color, vec3(0), vec3(1));
fColor = linearToSRGB(color); // linear to sRGB conversion
fColor = color;
}
......@@ -20,3 +20,15 @@ void outputTransparency(vec3 premulColor, float alpha)
fAccum = vec4(premulColor, alpha) * w;
fRevealage = alpha;
}
void outputFresnel(vec3 N, vec3 V, vec3 premulColor, float baseAlpha)
{
float dotNV = dot(N, V);
float F = pow(1.0 - abs(dotNV), 5.0); // manually?
float a = mix(baseAlpha, 1.0, F);
outputTransparency(premulColor / baseAlpha * a, a);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment