From a385dc36227057ee76312963dd08129819aaab16 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Sun, 14 Nov 2021 15:40:59 +0200 Subject: [PATCH] first iteration of the PBR shader some bugfixes --- CMakeLists.txt | 3 ++ light.cpp | 40 +++++++++---------- light.h | 2 +- pbr.frag | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ pbr.vert | 39 ++++++++++++++++++ pbrSample.cpp | 99 +++++++++++++++++++++++++++++++++++++++++++++ texture.frag | 48 +++++++++++----------- 7 files changed, 292 insertions(+), 45 deletions(-) create mode 100644 pbr.frag create mode 100644 pbr.vert create mode 100644 pbrSample.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 111e948..1c578ad 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,4 +94,7 @@ if(BUILD_SAMPLES) add_executable(shadowSample shadowSample.cpp) target_link_libraries(shadowSample ${LIB_NAME}) + + add_executable(pbrSample pbrSample.cpp) + target_link_libraries(pbrSample ${LIB_NAME}) endif() diff --git a/light.cpp b/light.cpp index 185e036..7dd4e7c 100755 --- a/light.cpp +++ b/light.cpp @@ -181,36 +181,36 @@ namespace vb01{ for(Material *m : materials){ Shader *shader = m->getShader(); shader->use(); - shader->setInt((int)type, "light[" + to_string(thisId) + "].type"); - shader->setVec3(color, "light[" + to_string(thisId) + "].color"); - shader->setFloat(nearPlane, "light[" + to_string(thisId) + "].near"); - shader->setFloat(farPlane, "light[" + to_string(thisId) + "].far"); + shader->setInt((int)type, "lights[" + to_string(thisId) + "].type"); + shader->setVec3(color, "lights[" + to_string(thisId) + "].color"); + shader->setFloat(nearPlane, "lights[" + to_string(thisId) + "].near"); + shader->setFloat(farPlane, "lights[" + to_string(thisId) + "].far"); int depthMapId = 10; - shader->setInt(depthMapId, "light[" + to_string(thisId) + "].depthMap"); - shader->setInt(depthMapId + 1, "light[" + to_string(thisId) + "].depthMapCube"); + shader->setInt(depthMapId, "lights[" + to_string(thisId) + "].depthMap"); + shader->setInt(depthMapId + 1, "lights[" + to_string(thisId) + "].depthMapCube"); depthMap->select(depthMapId + (type == POINT ? 1 : 0)); switch(type){ case POINT: - shader->setVec3(position, "light[" + to_string(thisId) + "].pos"); - shader->setFloat(attenuationValues.x, "light[" + to_string(thisId) + "].a"); - shader->setFloat(attenuationValues.y, "light[" + to_string(thisId) + "].b"); - shader->setFloat(attenuationValues.z, "light[" + to_string(thisId) + "].c"); + shader->setVec3(position, "lights[" + to_string(thisId) + "].pos"); + shader->setFloat(attenuationValues.x, "lights[" + to_string(thisId) + "].a"); + shader->setFloat(attenuationValues.y, "lights[" + to_string(thisId) + "].b"); + shader->setFloat(attenuationValues.z, "lights[" + to_string(thisId) + "].c"); break; case DIRECTIONAL: - shader->setMat4(proj * view, "light[" + to_string(thisId) + "].lightMat"); - shader->setVec3(direction, "light[" + to_string(thisId) + "].direction"); + shader->setMat4(proj * view, "lights[" + to_string(thisId) + "].lightMat"); + shader->setVec3(direction, "lights[" + to_string(thisId) + "].direction"); break; case SPOT: - shader->setMat4(proj * view, "light[" + to_string(thisId) + "].lightMat"); - shader->setVec3(position, "light[" + to_string(thisId) + "].pos"); - shader->setVec3(direction,"light[" + to_string(thisId) + "].direction"); - shader->setFloat(attenuationValues.x, "light["+to_string(thisId) + "].a"); - shader->setFloat(attenuationValues.y, "light["+to_string(thisId) + "].b"); - shader->setFloat(attenuationValues.z, "light["+to_string(thisId) + "].c"); - shader->setFloat(innerAngle, "light[" + to_string(thisId) + "].innerAngle"); - shader->setFloat(outerAngle, "light[" + to_string(thisId) + "].outerAngle"); + shader->setMat4(proj * view, "lights[" + to_string(thisId) + "].lightMat"); + shader->setVec3(position, "lights[" + to_string(thisId) + "].pos"); + shader->setVec3(direction,"lights[" + to_string(thisId) + "].direction"); + shader->setFloat(attenuationValues.x, "lights["+to_string(thisId) + "].a"); + shader->setFloat(attenuationValues.y, "lights["+to_string(thisId) + "].b"); + shader->setFloat(attenuationValues.z, "lights["+to_string(thisId) + "].c"); + shader->setFloat(innerAngle, "lights[" + to_string(thisId) + "].innerAngle"); + shader->setFloat(outerAngle, "lights[" + to_string(thisId) + "].outerAngle"); break; } } diff --git a/light.h b/light.h index 6240e1d..2eeb20c 100755 --- a/light.h +++ b/light.h @@ -47,7 +47,7 @@ namespace vb01{ Shader *depthMapShader; Texture *depthMap = nullptr; Node *node = nullptr; - Vector3 color, attenuationValues = Vector3(1.8, .7, 1); + Vector3 color = Vector3::VEC_IJK, attenuationValues = Vector3(1.8, .7, 1); float innerAngle = .707, outerAngle = .714, nearPlane = .1, farPlane = 100; unsigned int depthmapFBO, depthMapSize = 1024; }; diff --git a/pbr.frag b/pbr.frag new file mode 100644 index 0000000..6acfb70 --- /dev/null +++ b/pbr.frag @@ -0,0 +1,106 @@ +#version 330 core +const int numLights=1; +const float PI = 3.14159; + +in vec3 fragPos; +in vec3 tan; +in vec3 biTan; +in vec3 norm; +in vec2 texCoords; + +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 BrightColor; + +//0-POINT,1-DIRECTIONAL,2-SPOT + +struct Light{ + int type; + vec3 pos, color, direction; + float innerAngle, outerAngle; + float a, b, c, near, far; + sampler2D depthMap; + samplerCube depthMapCube; + mat4 lightMat; +}; + +struct Texture{ + float mixRatio; + sampler2D pastTexture, nextTexture; + bool animated; +}; + +uniform Light lights[numLights]; +uniform Texture textures[1]; + +uniform bool albedoMapEnabled; +uniform vec4 albedoColor; +uniform float roughness; +uniform float metalness; +uniform vec3 baseReflectivity; +uniform vec3 camPos; + +float trowbridgeReitz(vec3 n, vec3 h, float a){ + return a * a / (PI * pow(pow(max(dot(n, h), 0), 2) * (a * a - 1) + 1, 2)); +} + +float schlickGGX(vec3 vec1, vec3 vec2, float k){ + float dotProd = max(dot(vec1, vec2), 0); + return dotProd / (dotProd * (1 - k) + k); +} + +float geoSmith(vec3 n, vec3 v, vec3 l, float k){ + float ggx1 = schlickGGX(n, v, k); + float ggx2 = schlickGGX(n, l, k); + return ggx1 * ggx2; +} + +vec3 fresnelSchlick(vec3 v, vec3 h, vec3 f0){ + return f0 + (vec3(1) - f0) * pow(1 - max(dot(v, h), 0), 5); +} + +vec3 lambertDiffuse(vec4 color){ + return (color / PI).xyz; +} + +void main() { + vec4 finalColor = vec4(0, 0, 0, 1); + vec3 normal = norm; + vec3 viewDir = normalize(camPos - fragPos); + vec4 albedo = (albedoMapEnabled ? texture(textures[0].pastTexture, texCoords) : albedoColor); + + for(int i = 0; i < numLights; ++i){ + vec3 lightDir; + float dist = length(lights[i].pos - fragPos), attenuation = 1, a = lights[i].a, b = lights[i].b, c = lights[i].c; + + if(lights[i].type == 0){ + lightDir = normalize(lights[i].pos - fragPos); + attenuation = 1 / (a * dist * dist + b * dist + c); + } + else if(lights[i].type == 1){ + lightDir = -normalize(lights[i].direction); + } + else if(lights[i].type == 2){ + lightDir = normalize(lights[i].pos - fragPos); + attenuation = 1 / (a * dist * dist + b * dist + c); + } + + vec3 halfVec = normalize(lightDir + normal); + + float D = trowbridgeReitz(normal, halfVec, roughness * roughness); + float G = geoSmith(normal, halfVec, lightDir, 0.5 * roughness * roughness); + vec3 F = fresnelSchlick(viewDir, halfVec, baseReflectivity); + vec3 cookTor = D * G * F / max(4 * max(dot(viewDir, normal), 0) * max(dot(lightDir, normal), 0), .000001); + + vec3 fDiff = lambertDiffuse(albedo); + vec3 kDiff = (vec3(1) - F) * (1 - metalness); + + finalColor.rgb += (kDiff * fDiff + cookTor) * lights[i].color * attenuation * max(dot(lightDir, normal), 0); + } + + float brightness = dot(finalColor.rgb, vec3(0.2126, 0.7152, 0.0722)); + + if(brightness > 1) + BrightColor = vec4(finalColor.rgb, 1); + + FragColor = finalColor; +} diff --git a/pbr.vert b/pbr.vert new file mode 100644 index 0000000..b90458b --- /dev/null +++ b/pbr.vert @@ -0,0 +1,39 @@ +#version 330 core + +const int numBones = 1; +const int numVertGroups = 1; +const int numMaxInfluences = 4; +const int numShapeKeys = 1; + +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNorm; +layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in vec3 aTan; +layout (location = 4) in vec3 aBiTan; + +out vec3 fragPos, norm, tan, biTan; +out vec2 texCoords; + +struct Bone{ + float angle; + vec3 pos, trans, rotAxis, scale; + int vertGroup, parent; +}; + +uniform bool animated; +uniform Bone bones[numBones]; +uniform mat4 model; +uniform mat4 view; +uniform mat4 proj; +uniform float[numShapeKeys] shapeKeyFactors; + +void main(){ + mat3 normalMat = mat3(transpose(inverse(model))); + norm = (normalMat * aNorm); + tan = (normalMat * aTan); + biTan = (normalMat * aBiTan); + fragPos = vec3(model * vec4(aPos, 1)); + texCoords = aTexCoords; + + gl_Position = proj * view * model * vec4(aPos, 1); +} diff --git a/pbrSample.cpp b/pbrSample.cpp new file mode 100644 index 0000000..7026cdf --- /dev/null +++ b/pbrSample.cpp @@ -0,0 +1,99 @@ +#include "root.h" +#include "model.h" +#include "material.h" +#include "texture.h" +#include "light.h" +#include "box.h" + +#include + +using namespace vb01; +using namespace std; + +int main() { + const string PATH = "../", TEX_PATH = PATH + "samples/textures/", MODEL_PATH = PATH + "samples/models/"; + string skyboxTextures[] = { + TEX_PATH + "top.jpg", + TEX_PATH + "bottom.jpg", + TEX_PATH + "left.jpg", + TEX_PATH + "right.jpg", + TEX_PATH + "front.jpg", + TEX_PATH + "back.jpg" + }; + + /* + * Gets Root object to start the sample, + * also passes the base path for asset retrieval. + */ + Root *root = Root::getSingleton(); + root->start(800, 600, PATH, "Light sample"); + root->createSkybox(skyboxTextures); + root->setHDREnabled(true); + + Camera *cam = root->getCamera(); + cam->setPosition(Vector3(1, 1, 1) * 5); + cam->lookAt(Vector3(-1, -1, -1).norm(), Vector3(-1, 1, -1).norm()); + + Node *rootNode = root->getRootNode(); + + Model *model = new Model(MODEL_PATH + "teapot.vb"); + Material *mat = new Material(PATH + "pbr"); + mat->addBoolUniform("albedoMapEnabled", false); + mat->addVec4Uniform("albedoColor", Vector4(1, 1, 1, 1)); + mat->addFloatUniform("roughness", .1); + mat->addFloatUniform("metalness", .1); + mat->addVec3Uniform("baseReflectivity", Vector3(0.563, 0.579, 0.579)); + /* + mat = new Material(PATH + "texture"); + mat->addBoolUniform("texturingEnabled", true); + mat->addBoolUniform("lightingEnabled", true); + string fr[]{TEX_PATH + "bricks.jpg"}; + mat->addTexUniform("textures[0]", new Texture(fr, 1), true); + */ + model->setMaterial(mat); + rootNode->attachChild(model); + + { + Light *light = new Light(Light::POINT); + light->setAttenuationValues(.00001, .00001, 1); + light->setColor(Vector3(1, 0, 0)); + light->setInnerAngle(.1); + light->setOuterAngle(.1); + Node *lightNode = new Node(); + Box *b = new Box(Vector3(1, 1, 1) * .1); + Material *m = new Material(PATH + "texture"); + m->addBoolUniform("texturingEnabled", false); + m->addBoolUniform("lightingEnabled", false); + m->addVec4Uniform("diffuseColor", Vector4(1, 0, 0, 1)); + b->setMaterial(m); + lightNode->attachMesh(b); + lightNode->addLight(light); + rootNode->attachChild(lightNode); + lightNode->setPosition(Vector3(2, 3, 0)); + lightNode->setOrientation(Quaternion(1.57, Vector3::VEC_I)); + } + { + Light *light = new Light(Light::POINT); + light->setAttenuationValues(.00001, .00001, 1); + light->setColor(Vector3(0, 0, 1)); + light->setInnerAngle(.1); + light->setOuterAngle(.1); + Node *lightNode = new Node(); + Box *b = new Box(Vector3(1, 1, 1) * .1); + Material *m = new Material(PATH + "texture"); + m->addBoolUniform("texturingEnabled", false); + m->addBoolUniform("lightingEnabled", false); + m->addVec4Uniform("diffuseColor", Vector4(0, 0, 1, 1)); + b->setMaterial(m); + lightNode->attachMesh(b); + lightNode->addLight(light); + rootNode->attachChild(lightNode); + lightNode->setPosition(Vector3(-2, 3, 0)); + lightNode->setOrientation(Quaternion(1.57, Vector3::VEC_I)); + } + + while(true) + root->update(); + + return 0; +} diff --git a/texture.frag b/texture.frag index 38fb860..df9cc28 100755 --- a/texture.frag +++ b/texture.frag @@ -31,35 +31,34 @@ struct Texture{ uniform Texture textures[4]; uniform samplerCube environmentMap; -uniform Light light[numLights]; +uniform Light lights[numLights]; uniform bool lightingEnabled, texturingEnabled, normalMapEnabled, specularMapEnabled, castShadow, environmentMapEnabled; uniform vec4 diffuseColor, specularColor; uniform float shinyness, specularStrength; uniform vec3 camPos; float linDepth(float depth, int i){ - float z = depth * 2 - 1, near = light[i].near, far = light[i].far; + float z = depth * 2 - 1, near = lights[i].near, far = lights[i].far; return (2 * near * far) / (far + near - z * (far - near)); } float getShadow(int id){ - int type = light[id].type; + int type = lights[id].type; float shadow = 0, closestDepth = 0, currentDepth = 0, bias = .005, val = .7; if(type == 0){ - vec3 projDir = fragPos - light[id].pos; - closestDepth = texture(light[id].depthMapCube, projDir).r * light[id].far; + vec3 projDir = fragPos - lights[id].pos; + closestDepth = texture(lights[id].depthMapCube, projDir).r * lights[id].far; currentDepth = length(projDir); shadow = (currentDepth - bias > closestDepth) ? val : 0; } else{ - vec4 lightSpaceFragPos = light[id].lightMat * vec4(fragPos, 1); + vec4 lightSpaceFragPos = lights[id].lightMat * vec4(fragPos, 1); vec3 projCoords = (lightSpaceFragPos.xyz / lightSpaceFragPos.w) * .5 + .5; - closestDepth = texture(light[id].depthMap, projCoords.xy).r; + closestDepth = texture(lights[id].depthMap, projCoords.xy).r; currentDepth = projCoords.z; if(type == 2){ - closestDepth = linDepth(closestDepth, id); - currentDepth = linDepth(currentDepth, id); + closestDepth = linDepth(closestDepth, id); currentDepth = linDepth(currentDepth, id); } shadow = (currentDepth - bias > closestDepth) ? val : 0; if(projCoords.z > 1) @@ -68,7 +67,8 @@ float getShadow(int id){ return shadow; } -void main(){ vec4 finalColor = diffuseColor; +void main(){ + vec4 finalColor = diffuseColor; if(texturingEnabled){ vec4 textureColor = texture(textures[0].pastTexture, texCoords); @@ -93,36 +93,36 @@ void main(){ vec4 finalColor = diffuseColor; for(int i = 0; i < numLights; i++){ vec3 lightDir = vec3(0), viewDir = normalize(camPos - fragPos); float attenuation = 1.0; - float a = light[i].a, b = light[i].b, c = light[i].c, dist = length(fragPos - light[i].pos), factor, coef = 1; + float a = lights[i].a, b = lights[i].b, c = lights[i].c, dist = length(fragPos - lights[i].pos), factor, coef = 1; - if(light[i].type == 0){ - lightDir = normalize(light[i].pos - fragPos); + if(lights[i].type == 0){ + lightDir = normalize(lights[i].pos - fragPos); attenuation = 1.0 / (a * dist * dist + b * dist + c); } - else if(light[i].type == 1){ - lightDir = -normalize(light[i].direction); + else if(lights[i].type == 1){ + lightDir = -normalize(lights[i].direction); } - else if(light[i].type == 2){ - lightDir = normalize(light[i].pos - fragPos); - float innerAngle = light[i].innerAngle; - float angle = acos(dot(-lightDir, light[i].direction)); - float outerAngle = light[i].outerAngle; - if(light[i].innerAngle <= angle && angle <= light[i].outerAngle){ + else if(lights[i].type == 2){ + lightDir = normalize(lights[i].pos - fragPos); + float innerAngle = lights[i].innerAngle; + float angle = acos(dot(-lightDir, lights[i].direction)); + float outerAngle = lights[i].outerAngle; + if(lights[i].innerAngle <= angle && angle <= lights[i].outerAngle){ coef = (angle - outerAngle) / (innerAngle - outerAngle); } - if(angle > light[i].outerAngle){ + if(angle > lights[i].outerAngle){ continue; } } factor = max(dot(lightDir, normal), 0.); - diffuseCol += light[i].color * (factor * attenuation * coef); + diffuseCol += lights[i].color * (factor * attenuation * coef); if(specularMapEnabled){ float specularSample = texture(textures[2].pastTexture, texCoords).r; vec3 halfwayVec = normalize(lightDir + viewDir); float spec = pow(max(dot(normal, halfwayVec), 0), shinyness); - specularCol += specularStrength * spec * specularSample * light[i].color; + specularCol += specularStrength * spec * specularSample * lights[i].color; } }