Assistance understanding short watercolor shader code? - glsl

I am new to shaders and trying to understand how some of them work. I found one which looks very cool and is less than 20 lines, but I cannot figure out what's actually going on with all the single-letter variable names.
Would somebody be able to explain how this works?
Here you can see the output of the shader with code.
And here is the code:
precision highp float;
uniform float time;
uniform vec2 resolution;
varying vec2 surfacePosition;
void main() {
vec2 u = (1.5 - 4.0) * surfacePosition;
vec3 ht = smoothstep(0.0, 2.0, 12.0 - dot(u, u)) * vec3(u * 0.02, -1.0);
vec3 n = 100.0 * normalize(ht - vec3(0.0, -0.5 * fract(0.015), 0.65));
vec3 p = n;
for (float i = 0.0; i <= 10.0; i++) {
p = 10.0 * n + vec3(cos(0.255 * time - i - p.x) + cos(0.255 * time + i - p.y), sin(i - p.y) + cos(i + p.x), 9);
p.xy = cos(i) * p.xy + sin(i) * vec2(p.y, -p.x);
}
float tx = 5.0 * sqrt(dot(vec3(2, 0.2, 4), -p));
gl_FragColor = vec4(pow(sin(vec3(9, 0.4, 1.1) - tx) * 0.55 + 0.25, vec3(0.5)), 2.5);

Related

Dark spot where light should be when attempting to code pbr shader

I keep having this bug where there's a black spot right where I would assume the model is supposed to be brightest. I pulled an all-nighter trying to get this to work, but no avail.
I've been following this tutuorial https://learnopengl.com/PBR/Lighting, and referencing this code as well https://github.com/Nadrin/PBR/blob/master/data/shaders/hlsl/pbr.hlsl
As far as I can tell, the math operations I'm doing are identical but they don't produce the intended results. Along with the dark spots, roughness seems to not effect the end result whatsoever, even though I use it in several places that effect the end result.
Here's the code I'm using, all inputs are in world chordinates:
vec3 gammaCorrect(vec3 color)
{
color = color / (color + vec3(1.0));
return pow(color, vec3(1.0/2.2));
}
vec3 shadeDiffuse(vec3 color, vec3 position, vec3 normal)
{
vec3 lightHue = vec3(0,0,0);
for(uint i = 0; i < plb.numLights; ++i)
{
float sqrdist = distance(plb.lights[i].position, position);
sqrdist *= sqrdist;
float b = max(0, dot(normalize(plb.lights[i].position - position), normal) * max(0, plb.lights[i].color.a * (1 / sqrdist)));
lightHue += plb.lights[i].color.xyz * b;
}
color *= lightHue;
return gammaCorrect(color);
}
#ifndef PI
const float PI = 3.14159265359;
#endif
float DistributionGGX(vec3 normal, vec3 viewVec, float roughness)
{
float a2 = pow(roughness, 4);
float NdotH = max(dot(normal, viewVec), 0.0);
float denom = (NdotH*NdotH * (a2 - 1.0) + 1.0);
return a2 / (PI * denom * denom);
}
float GeometrySchlickGGX(float dotp, float roughness)
{
return dotp / (dotp * (1.0 - roughness) + roughness);
}
float GeometrySmith(vec3 normal, vec3 viewVec, vec3 lightVec, float roughness)
{
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
return GeometrySchlickGGX(max(dot(normal, viewVec), 0.0), k) * GeometrySchlickGGX(max(dot(normal, lightVec), 0.0), k);
}
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 shadePBR(vec3 albedo, vec3 position, vec3 cameraPos, vec3 normal, float roughness, float metallic)
{
vec3 viewVec = normalize(cameraPos - position);
const vec3 F0 = mix(vec3(0.03), albedo, metallic);
vec3 lightHue = vec3(0);
for(uint i = 0; i < plb.numLights; ++i)
{
// radiance
vec3 lightVec = normalize(plb.lights[i].position - position);
vec3 halfVec = normalize(viewVec + lightVec);
float distance = length(plb.lights[i].position - position);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = plb.lights[i].color.xyz * attenuation * max(plb.lights[i].color.a, 0);
// brdf
float NDF = DistributionGGX(halfVec, normal, roughness);
float G = GeometrySmith(normal, viewVec, lightVec, roughness);
vec3 F = fresnelSchlick(max(dot(halfVec, viewVec), 0.0), F0);
vec3 kD = mix(vec3(1)-F, vec3(0), metallic);
float viewDot = max(dot(normal, viewVec), 0.0);
float lightDot = max(dot(normal, lightVec), 0.0);
vec3 specular = (NDF * G * F) / (4.0 * max(viewDot * lightDot, 0.000001));
// add to hue
lightHue += (kD * albedo / PI + specular) * radiance * lightDot;
}
//Add in ambient here later
vec3 color = lightHue;
return gammaCorrect(color);
}
I'm going to go sleep now, thanks for any help in advance.
So turns out I'm very stupid. Problem was that I was trying to grab the camera position from the render matrix, and as I have found out, you can't really grab a clean position from that without fully disassembling it, instead of just grabbing a few indexes from it. Passed camera position with a uniform instead and code immediately worked perfectly.

Reproducing the dot product function in a for loop in GLSL does not output wanted results

In the code below you will find, within the for loop with 10 iterations, a commented out line that involves calculating a dot product by using the GLSL dot() function.
Below the commented line I am trying to reproduce the dot product(please look in the code), but the ongoing results are black screen.
#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
void main()
{
float time_ = (time+10.) * 30.0;
vec2 uv = (-resolution.xy + 2.0 * gl_FragCoord.xy ) / resolution;
float s = 0.0;
float v = -0.010;
float t = time_*0.005;
uv.x += sin(t) * 0.15;
vec3 color = vec3(0.0);
vec3 init = vec3(-0.25, -0.25 + sin(time_ * 0.001) * 0.4, floor(time_) * 0.0008);
for (int r = 0; r < 100; r++)
{
vec3 p = init + s * vec3(uv, 0.143);
p.z = mod(-p.z, 2.0)-1.0;
for (int i=0; i < 10; i++)
{
//p = abs(p * 2.003) / dot(p, p) - 0.75;
p.x = abs(p.x * 2.003) / (p.x*p.x+p.y*p.y+p.z*p.z) - 0.75;
p.y = abs(p.y * 2.003) / (p.x*p.x+p.y*p.y+p.z*p.z) - 0.75;
p.z = abs(p.z * 2.003) / (p.x*p.x+p.y*p.y+p.z*p.z) - 0.75;
}
//v += length(p * p) * smoothstep(0.0, 0.1, 0.9 - s) * .0005;
v += sqrt((p.x*p.x*p.x*p.x)+(p.y*p.y*p.y*p.y)+(p.z*p.z*p.z*p.z)) * smoothstep(0.0, 0.1, 0.9 - s) * .0005;
color+=vec3(v*0.02);
s += .005;
}
gl_FragColor = vec4(color,1.0);
}
The purpose why I am doing this maybe looks like it's unimportant for many here, but I am really trying to understand how the dot() calculates the correct output in this case in the for loop, knowing the fact that the dot() function itself is correctly reproduced.
I ask for a thorough explanation on this matter with code example if possible.

Few problems with BRDF using Beckmann and GGX/Trowbridge-Reitz distribution for comparison

I have been trying to wrap my head around physical based rendering these last 2.5 weeks and so far I managed to learn a lot, ask a lot of questions, and have some results, although I still have few problems that I would like to fix but the last few days I am stuck. I am want to continue working/learning more but now I don't know what else to do or how to proceed further, thus I need some guidance :(
One of the first problems that I can not figure out what is happening is when I get close to a shape. There is a cut-off problem with BRDF function that I have implemented. The second and third row are BRDF functions using Spherical Gaussian for Fresnel, and Schlick approximation. The second row Beckmann distribution NDF and the third one uses GGX/Trowbridge-Reitz as NDF.
I started implementing this referring to "Real Shading in Unreal Engine 4" and few other posts found while Google-ing.
What I believe the remaining things to do are:
How to blend diffuse, reflection, and speculal better
Fix the problem with the BRDF cut-off problem
Evaluate if my shaders are producing good results based on the equation (it is the first time for me going this way and some comments would be very helpful as a guide on how to proceed in tweaking things)
Fix specular factor in Phong (first row) shader, now I use material roughness as a blend factor when I mix Phong, skybox reflection and diffuse
The code I use for BRDF's is
// geometry term Cook Torrance
float G(float NdotH, float NdotV, float VdotH, float NdotL) {
float NH2 = 2.0 * NdotH;
float g1 = (NH2 * NdotV) / VdotH;
float g2 = (NH2 * NdotL) / VdotH;
return min(1.0, min(g1, g2));
}
// Fresnel reflection term, Schlick approximation
float R_Fresnel(float VdotH) {
return F0 + (1.0 - F0) * pow(2, (-5.55473 * (VdotH)-6.98316) * (VdotH));
}
// Normal distribution function, GGX/Trowbridge-Reitz
float D_GGX(float NdotH, float roughtness2) {
float a = roughtness2 * roughtness2;
float a2 = a*a;
float t = ((NdotH * NdotH) * (a2 - 1.0) + 1.0);
return a2 / (PI * t * t);
}
// Normal distribution function, Beckmann distribution
float D_Beckmann(float NdotH, float mSquared) {
float r1 = 1.0 / (4.0 * mSquared * pow(NdotH, 4.0));
float r2 = (NdotH * NdotH - 1.0) / (mSquared * NdotH * NdotH);
return (r1 * exp(r2));
}
// COOK TORRANCE BRDF
vec4 cookTorrance(Light light, vec3 direction, vec3 normal) {
// do the lighting calculation for each fragment.
float NdotL = max(dot(normal, direction), 0.0);
float specular = 0.0;
if (NdotL > 0.0)
{
vec3 eyeDir = normalize(cameraPosition);
// calculate intermediary values
vec3 halfVector = normalize(direction + eyeDir);
float NdotH = max(dot(normal, halfVector), 0.0);
float NdotV = max(dot(normal, eyeDir), 0.0);
float VdotH = max(dot(eyeDir, halfVector), 0.0);
float matShininess = (material.shininess / 1000.0);
float mSquared = (0.99 - matShininess) * (0.99 - matShininess);
float geoAtt = G(NdotH, NdotV, VdotH, NdotL);
float roughness = D_Beckmann(NdotH, mSquared);
float fresnel = R_Fresnel(VdotH);
specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * PI);
}
vec3 finalValue = light.color * NdotL * (k + specular * (1.0 - k));
return vec4(finalValue, 1.0);
}
vec4 cookTorrance_GGX(Light light, vec3 direction, vec3 normal) {
// do the lighting calculation for each fragment.
float NdotL = max(dot(normal, direction), 0.0);
float specular = 0.0;
if (NdotL > 0.0)
{
vec3 eyeDir = normalize(cameraPosition);
// calculate intermediary values
vec3 halfVector = normalize(direction + eyeDir);
float NdotH = max(dot(normal, halfVector), 0.0);
float NdotV = max(dot(normal, eyeDir), 0.0);
float VdotH = max(dot(eyeDir, halfVector), 0.0);
float matShininess = (material.shininess / 1000.0);
float mSquared = (0.99 - matShininess) * (0.99 - matShininess);
float geoAtt = G(NdotH, NdotV, VdotH, NdotL);
// NDF CHANGED TO GGX
float roughness = D_GGX(NdotH, mSquared);
float fresnel = R_Fresnel(VdotH);
specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * PI);
}
vec3 finalValue = light.color * NdotL * (k + specular * (1.0 - k));
return vec4(finalValue, 1.0);
}
void main() {
//vec4 tempColor = vec4(material.diffuse, 1.0);
vec4 tempColor = vec4(0.1);
// interpolating normals will change the length of the normal, so renormalize the normal.
vec3 normal = normalize(Normal);
vec3 I = normalize(Position - cameraPosition);
vec3 R = reflect(I, normalize(Normal));
vec4 reflection = texture(skybox, R);
// fix blending
float shininess = (material.shininess / 1000.0);
vec4 tempFinalDiffuse = mix(tempColor, reflection, shininess);
vec4 finalValue = cookTorrance_GGX(directionalLight.light, directionalLight.position, normal) + tempFinalDiffuse;
// OR FOR COOK TORRANCE IN THE OTHER SHADER PROGRAM
//vec4 finalValue = cookTorrance(directionalLight.light, directionalLight.position, normal) + tempFinalDiffuse;
gl_FragColor = finalValue;
//gl_FragColor = vec4(1.0); // TESTING AND DEBUGGING FRAG OUT
}
The results i have so far are lik in pictures below
EDIT :: I managed to solve few problems and implement environment sampling given in "Real Shading in Unreal Engine 4" but still I just cant figure out why I have that cut-off problem and I have a problem with reflection now after sampling. :(
Also I moved Phong that i tough in books and online tutorial to BDRF Blinn-Phong for better comparison.
My shader now looks like this.
vec4 brdf_GGX(Light light, vec3 direction, vec3 normal) {
float specular = 0.0;
float matShininess = 1.0 - (material.shininess / 1000.0);
vec2 randomPoint;
vec4 finalColor = vec4(0.0);
vec4 totalLambert = vec4(0.0);
const uint numberSamples = 32;
for (uint sampleIndex = 0; sampleIndex < numberSamples; sampleIndex++)
{
randomPoint = hammersley2d(sampleIndex, numberSamples);
vec3 H = ImportanceSampleGGX(randomPoint, matShininess, normal);
vec3 L = 2.0 * dot(normal, H) * H - normal;
vec3 R = reflect(L, normalize(normal));
totalLambert += texture(skybox, -R);
}
totalLambert = totalLambert / numberSamples;
float NdotL = max(dot(normal, direction), 0.0);
if (NdotL > 0.0)
{
vec3 eyeDir = normalize(cameraPosition);
// calculate intermediary values
vec3 halfVector = normalize(direction + eyeDir);
float NdotH = max(dot(normal, halfVector), 0.0);
float NdotV = max(dot(normal, eyeDir), 0.0);
float VdotH = max(dot(eyeDir, halfVector), 0.0);
float mSquared = clamp(matShininess * matShininess, 0.01, 0.99);
float geoAtt = G(NdotH, NdotV, VdotH, NdotL);
float roughness = D_Beckmann(NdotH, mSquared);
float fresnel = R_Fresnel(VdotH);
specular = (fresnel * geoAtt * roughness) / (NdotV * NdotL * PI);
}
vec3 finalValue = light.color * NdotL * (k + specular * (1.0 - k));
return vec4(finalValue, 1.0) * totalLambert;
}
Current results look like this (NOTE: I used skybox sampling only in the third GGX model, do the same for other shaders tomorrow)
EDIT:: OK i am figuring out what is happening but still i can not fix it. I have problems when sampling. I have no idea how to translate normalized ray to proper cube map reflection after sampling. If you can notice in pictures I lost the correct reflection that sphere does to environment map. I just have a simple/flat texture on each sphere and now I have no idea how to fix that.

Physically based shader not producing desired results

Over the past ~2-3 weeks, i've been learning about Physically Based Shading and I just cannot wrap my head around some of the problems I'm having.
Fragment Shader
#version 430
#define PI 3.14159265358979323846
// Inputs
in vec3 inputNormal;
vec3 fNormal;
// Material
float reflectance = 1.0; // 0 to 1
float roughness = 0.5;
vec3 specularColor = vec3(1.0, 1.0, 1.0); // f0
// Values
vec3 lightVector = vec3(1, 1, 1); // Light (l)
vec3 eyeVector = vec3(2.75, 1.25, 1.25); // Camera (v)
vec3 halfVector = normalize(lightVector + eyeVector); // L + V / |L + V|
out vec4 fColor; // Output Color
// Specular Functions
vec3 D(vec3 h) // Normal Distribution Function - GGX/Trowbridge-Reitz
{
float alpha = roughness * roughness;
float alpha2 = alpha * alpha;
float NoH = dot(fNormal, h);
float finalTerm = ((NoH * NoH) * (alpha2 - 1.0) + 1.0);
return vec3(alpha2 / (PI * (finalTerm * finalTerm)));
}
vec3 Gsub(vec3 v) // Sub Function of G
{
float k = ((roughness + 1.0) * (roughness + 1.0)) / 8;
return vec3(dot(fNormal, v) / ((dot(fNormal, v)) * (1.0 - k) + k));
}
vec3 G(vec3 l, vec3 v, vec3 h) // Geometric Attenuation Term - Schlick Modified (k = a/2)
{
return Gsub(l) * Gsub(v);
}
vec3 F(vec3 v, vec3 h) // Fresnel - Schlick Modified (Spherical Gaussian Approximation)
{
vec3 f0 = specularColor; // right?
return f0 + (1.0 - f0) * pow(2, (-5.55473 * (dot(v, h)) - 6.98316) * (dot(v, h)));
}
vec3 specular()
{
return (D(halfVector) * F(eyeVector, halfVector) * G(lightVector, eyeVector, halfVector)) / 4 * ((dot(fNormal, lightVector)) * (dot(fNormal, eyeVector)));
}
vec3 diffuse()
{
float NoL = dot(fNormal, lightVector);
vec3 result = vec3(reflectance / PI);
return result * NoL;
}
void main()
{
fNormal = normalize(inputNormal);
fColor = vec4(diffuse() + specular(), 1.0);
//fColor = vec4(D(halfVector), 1.0);
}
So far I have been able to fix up some things and now I get a better result.
However it now seems clear that the highlight is way too big; this originates from the normal distribution function (Specular D).
Your coding of GGX/Trowbridge-Reitz is wrong:
vec3 NxH = fNormal * h;
The star * means a term by term product where you want a dot product
Also
float alphaTerm = (alpha * alpha - 1.0) + 1.0;
Is not correct since the formula multiplies n.m by (alpha * alpha - 1.0) before adding 1.0. Yours formula is equal to alpha*alpha!
Try:
// Specular
vec3 D(vec3 h) // Normal Distribution Function - GGX/Trowbridge-Reitz
{
float alpha = roughness * roughness;
float NxH = dot(fNormal,h);
float alpha2 = alpha*alpha;
float t = ((NxH * NxH) * (alpha2 - 1.0) + 1.0);
return alpha2 / (PI * t * t);
}
In many other places you use * instead of dot. You need to correct all these. Also, check for your formulas, many seem incorrect.

Ashikhmin-Shirley model implementation: ugly result

I am trying to implement the Ashikhmin-Shirley model using these formulas:
This is the GLSL 1.2 fragment shader code:
uniform vec4 materialAmbient, materialDiffuse, materialSpecular;
uniform float materialShininess;
uniform vec4 lightAmbient, lightDiffuse, lightSpecular, lightPosition;
varying vec3 P,N;
float pi= 3.1415926535;
vec4 Fd(float NdotV, float NdotL) {
vec4 fd= (28.0 * materialDiffuse * lightDiffuse) / (23.0 * pi) * (1.0 - materialSpecular * lightSpecular);
fd*= 1.0 - pow(1.0-NdotV/2.0,5.0);
fd*= 1.0 - pow(1.0-NdotL/2.0, 5.0);
return fd;
}
vec4 Fr(float u, vec4 specular) {
return specular + (1.0-specular) * pow(1.0 - u, 5.0);
}
// f= phi
vec4 Fs(float VdotH, float NdotH, float NdotL, float NdotV, float et, float eb, float f) {
vec4 fs= Fr(VdotH, materialSpecular * lightSpecular);
fs*= sqrt((et+1.0) * (eb+1.0)) / (8.0 * pi);
fs*= pow(NdotH, et*pow(cos(f),2.0) + eb*pow(sin(f),2.0)) / (VdotH * max(NdotL, NdotV));
return fs;
}
void main(void) {
vec3 L= normalize(vec3(lightPosition) - P);
vec3 V= cameraPosition;
vec3 H= normalize(L+V);
float NdotL= max(dot(N,L),0.0);
float NdotV= max(dot(N,V),0.0);
float NdotH= max(dot(N,H),0.0);
float VdotH= max(dot(V,H),0.0);
gl_FragColor= Fd(NdotV, NdotL) + Fs(VdotH, NdotH, NdotL, NdotV, 128.0,128.0,1.0);
}
I already checked and it seems like all the uniforms and varying are passed in the right way, I pass P and N from the vertex shader. The variables are:
Light direction: L;
Surface normal: N;
Camera direction: V;
Half vector: H;
Fragment position: P.
The uniforms I pass are:
light{Specular | Diffuse | Ambient} : 0xffffff (converted to a rgba vector of course);
materialAmbient: 0x543807 ;
materialDiffuse: 0xc6901d;
materialSpecular: 0xfdefce;
materialShininess: 27.8974.
This is the result I get:
Which seems very strange to me, I've seen other images of Ashkhmin-Shirley implementations on the web, and they aren't similar. This is an example:
I want one like this !! Maybe I am using wrong values of phi and other values? Or there's something wrong in the formula?
I think you might be missing braces here:
vec4 fd= (28.0 * materialDiffuse * lightDiffuse) / (23.0 * pi) * (1.0 - materialSpecular * lightSpecular);
try
vec4 fd= ((28.0 * materialDiffuse * lightDiffuse) / (23.0 * pi)) * (1.0 - materialSpecular * lightSpecular);
instead.