Added geometry shader to pipeline so I can calculate normal data on the GPU

This commit is contained in:
Warwick 2022-07-27 14:30:24 +01:00
parent 532382b1f9
commit 5b1aba34df
6 changed files with 122 additions and 44 deletions

View file

@ -0,0 +1,33 @@
#version 330 core
layout (triangles) in;
layout (triangle_strip) out;
in vec2 gtexCoord[];
in vec3 gnormCoord[];
in vec3 gWorldPos[];
out vec2 texCoord;
out vec3 normCoord;
out vec3 WorldPos;
void main()
{
gl_Position = gl_in[0].gl_Position;
texCoord = gtexCoord[0];
normCoord = gnormCoord[0];
WorldPos = gWorldPos[0];
EmitVertex();
gl_Position = gl_in[1].gl_Position;
texCoord = gtexCoord[1];
normCoord = gnormCoord[1];
WorldPos = gWorldPos[1];
EmitVertex();
gl_Position = gl_in[2].gl_Position;
texCoord = gtexCoord[2];
normCoord = gnormCoord[2];
WorldPos = gWorldPos[2];
EmitVertex();
EndPrimitive();
};

View file

@ -1,10 +1,10 @@
#version 330 core #version 330 core
out vec4 FragColor; out vec4 FragColor;
in vec2 ourTexCoord; in vec2 texCoord;
in vec3 ourNormCoord; in vec3 normCoord;
in vec3 WorldPos; in vec3 WorldPos;
in mat4 TBN; //in mat4 TBN;
// TODO: make temporary hard coded world/camera pos dynamic // TODO: make temporary hard coded world/camera pos dynamic
//uniform vec3 WorldPos ; //uniform vec3 WorldPos ;
@ -34,48 +34,48 @@ const float PI = 3.14159265359;
vec3 fresnelSchlick(float cosTheta, vec3 F0) vec3 fresnelSchlick(float cosTheta, vec3 F0)
{ {
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0); return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
} }
float DistributionGGX(vec3 N, vec3 H, float roughness) float DistributionGGX(vec3 N, vec3 H, float roughness)
{ {
float a = roughness*roughness; float a = roughness*roughness;
float a2 = a*a; float a2 = a*a;
float NdotH = max(dot(N, H), 0.0); float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH; float NdotH2 = NdotH*NdotH;
float num = a2; float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0); float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom; denom = PI * denom * denom;
return num / denom; return num / denom;
} }
float GeometrySchlickGGX(float NdotV, float roughness) float GeometrySchlickGGX(float NdotV, float roughness)
{ {
float r = (roughness + 1.0); float r = (roughness + 1.0);
float k = (r*r) / 8.0; float k = (r*r) / 8.0;
float num = NdotV; float num = NdotV;
float denom = NdotV * (1.0 - k) + k; float denom = NdotV * (1.0 - k) + k;
return num / denom; return num / denom;
} }
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{ {
float NdotV = max(dot(N, V), 0.0); float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0); float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2; return ggx1 * ggx2;
} }
vec3 normal(){ vec3 normal(){
// load and invert normal // load and invert normal
vec3 normal = normalize(texture(texture_normal1, ourTexCoord).rgb * 2.0 - 1.0); vec3 normal = normalize(texture(texture_normal1, texCoord).rgb * 2.0 - 1.0);
normal = (TBN * vec4(normal, 1.0)).xyz; //normal = (TBN * vec4(normal, 1.0)).xyz;
//TODO: Make the normal vector match the matrix of the rest of the model by //TODO: Make the normal vector match the matrix of the rest of the model by
//actually calculating the TBN //actually calculating the TBN
@ -92,7 +92,7 @@ vec3 PBR(vec3 albedo, float roughness, float metallic, float ao)
//vec3 lightColor = vec3(1.0, 1.0, 1.0) - sin(tick / 90); //vec3 lightColor = vec3(1.0, 1.0, 1.0) - sin(tick / 90);
vec3 lightColor = vec3(13.47, 11.31, 10.79); vec3 lightColor = vec3(13.47, 11.31, 10.79);
vec3 N = normalize(ourNormCoord); vec3 N = normalize(normCoord);
vec3 V = normalize(CameraPos - WorldPos); vec3 V = normalize(CameraPos - WorldPos);
N = (N + normal()) / 2; N = (N + normal()) / 2;
//N = normal(); For seeing if normal map tracks with light. //N = normal(); For seeing if normal map tracks with light.
@ -136,12 +136,12 @@ vec3 PBR(vec3 albedo, float roughness, float metallic, float ao)
void main() void main()
{ {
vec3 albedo; vec3 albedo;
albedo.r = pow(texture(texture_diffuse1, ourTexCoord).r, 2.2); albedo.r = pow(texture(texture_diffuse1, texCoord).r, 2.2);
albedo.g = pow(texture(texture_diffuse1, ourTexCoord).g, 2.2); albedo.g = pow(texture(texture_diffuse1, texCoord).g, 2.2);
albedo.b = pow(texture(texture_diffuse1, ourTexCoord).b, 2.2); albedo.b = pow(texture(texture_diffuse1, texCoord).b, 2.2);
float roughness = texture(texture_rma1, ourTexCoord).r; float roughness = texture(texture_rma1, texCoord).r;
float metallic = texture(texture_rma1, ourTexCoord).g; float metallic = texture(texture_rma1, texCoord).g;
float ao = texture(texture_rma1, ourTexCoord).b; float ao = texture(texture_rma1, texCoord).b;
FragColor = vec4(PBR(albedo, roughness, metallic, ao), 1.0); FragColor = vec4(PBR(albedo, roughness, metallic, ao), 1.0);
} }

View file

@ -6,23 +6,20 @@ layout (location = 2) in vec2 aTexCoord;
uniform mat4 MVP; uniform mat4 MVP;
uniform mat4 Model; uniform mat4 Model;
out vec2 ourTexCoord; out vec2 gtexCoord;
out vec3 ourNormCoord; out vec3 gnormCoord;
//Pbr //Pbr
out vec3 WorldPos; out vec3 gWorldPos;
//Normals //Normals
out mat4 TBN;
void main() void main()
{ {
gl_Position = MVP * Model * vec4(aPos, 1.0); gl_Position = MVP * Model * vec4(aPos, 1.0);
ourNormCoord = aNormal; gnormCoord = aNormal;
ourTexCoord = aTexCoord; gtexCoord = aTexCoord;
TBN = Model;
// Calculate position of fragment // Calculate position of fragment
WorldPos = vec3(Model * vec4(aPos, 1.0)); gWorldPos = vec3(Model * vec4(aPos, 1.0));
}; };

View file

@ -1,37 +1,54 @@
#include "ShaderLoader.h" #include "ShaderLoader.h"
#include <iostream> #include <iostream>
ShaderLoader::ShaderLoader(const char *vertexPath, const char *fragmentPath) { // This function always expects vertex and fragment shaders, if a geometry
// shader is present then it will also compile in that boi too.
// TODO: Make this code more readable and reduce the number of if statements.
void ShaderLoader::loadShader(const char *vertexPath, const char *fragmentPath,
const char *geometryPath) {
std::string vertexCode; std::string vertexCode;
std::string fragmentCode; std::string fragmentCode;
std::string geometryCode;
std::ifstream vShaderFile; std::ifstream vShaderFile;
std::ifstream fShaderFile; std::ifstream fShaderFile;
std::ifstream gShaderFile;
vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
if (geometryPath != nullptr)
gShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try { try {
// Open Files // Open Files
vShaderFile.open(vertexPath); vShaderFile.open(vertexPath);
fShaderFile.open(fragmentPath); fShaderFile.open(fragmentPath);
std::stringstream vShaderStream, fShaderStream; if (geometryPath != nullptr)
gShaderFile.open(geometryPath);
std::stringstream vShaderStream, fShaderStream, gShaderStream;
// read file's buffer contents into streams // read file's buffer contents into streams
vShaderStream << vShaderFile.rdbuf(); vShaderStream << vShaderFile.rdbuf();
fShaderStream << fShaderFile.rdbuf(); fShaderStream << fShaderFile.rdbuf();
if (geometryPath != nullptr)
gShaderStream << gShaderFile.rdbuf();
// close file handlers // close file handlers
vShaderFile.close(); vShaderFile.close();
fShaderFile.close(); fShaderFile.close();
if (geometryPath != nullptr)
gShaderFile.close();
// convert stream into string // convert stream into string
vertexCode = vShaderStream.str(); vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str(); fragmentCode = fShaderStream.str();
if (geometryPath != nullptr)
geometryCode = gShaderStream.str();
} catch (std::string e) { } catch (std::string e) {
error.crash("Failed to read shaderfiles", e); error.crash("Failed to read shaderfiles", e);
} }
const char *vShaderCode = vertexCode.c_str(); const char *vShaderCode = vertexCode.c_str();
const char *fShaderCode = fragmentCode.c_str(); const char *fShaderCode = fragmentCode.c_str();
const char *gShaderCode = geometryCode.c_str();
// Compile shaders // Compile shaders
unsigned int vertex, fragment; unsigned int vertex, fragment, geometry;
int success; int success;
char infoLog[512]; char infoLog[512];
@ -60,10 +77,25 @@ ShaderLoader::ShaderLoader(const char *vertexPath, const char *fragmentPath) {
error.crash("Fragment shader compilation failed", infoLog); error.crash("Fragment shader compilation failed", infoLog);
} }
// create geometry shader
if (geometryPath != nullptr) {
geometry = glCreateShader(GL_GEOMETRY_SHADER);
// compile fragment shader
glShaderSource(geometry, 1, &gShaderCode, NULL);
glCompileShader(geometry);
// https://learnopengl.com/Getting-started/Hello-Triangle
glGetShaderiv(geometry, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(geometry, 512, NULL, infoLog);
error.crash("Geometry shader compilation failed", infoLog);
}
}
// shader Program // shader Program
ID = glCreateProgram(); ID = glCreateProgram();
glAttachShader(ID, vertex); glAttachShader(ID, vertex);
glAttachShader(ID, fragment); glAttachShader(ID, fragment);
if (geometryPath != nullptr)
glAttachShader(ID, geometry);
glLinkProgram(ID); glLinkProgram(ID);
// print linking errors if any // print linking errors if any
glGetProgramiv(ID, GL_LINK_STATUS, &success); glGetProgramiv(ID, GL_LINK_STATUS, &success);
@ -76,6 +108,16 @@ ShaderLoader::ShaderLoader(const char *vertexPath, const char *fragmentPath) {
// necessary // necessary
glDeleteShader(vertex); glDeleteShader(vertex);
glDeleteShader(fragment); glDeleteShader(fragment);
glDeleteShader(geometry);
}
ShaderLoader::ShaderLoader(const char *vertexPath, const char *fragmentPath,
const char *geometryPath) {
this->loadShader(vertexPath, fragmentPath, geometryPath);
}
ShaderLoader::ShaderLoader(const char *vertexPath, const char *fragmentPath) {
this->loadShader(vertexPath, fragmentPath, nullptr);
} }
void ShaderLoader::use() { glUseProgram(ID); } void ShaderLoader::use() { glUseProgram(ID); }

View file

@ -16,9 +16,14 @@ private:
unsigned int ID; unsigned int ID;
Error error = Error("ShaderLoader"); Error error = Error("ShaderLoader");
void loadShader(const char *vertexPath, const char *fragmentPath,
const char *geometryPath);
public: public:
// constructor builds shader // constructor builds shader
ShaderLoader(const char *vertexPath, const char *fragmentPath); ShaderLoader(const char *vertexPath, const char *fragmentPath);
ShaderLoader(const char *vertexPath, const char *fragmentPath,
const char *geometryPath);
// use the shader // use the shader
void use(); void use();

View file

@ -71,7 +71,8 @@ int main(int argc, char **argv) {
SDL_Event input; SDL_Event input;
ShaderLoader shader(ROOT_DIR "data/shaders/pbrVertex.glsl", ShaderLoader shader(ROOT_DIR "data/shaders/pbrVertex.glsl",
ROOT_DIR "data/shaders/pbrFragment.glsl"); ROOT_DIR "data/shaders/pbrFragment.glsl",
ROOT_DIR "data/shaders/geometry.glsl");
// Model backpack(std::string(ROOT_DIR) + // Model backpack(std::string(ROOT_DIR) +
// std::string("data/models/backpack/backpack.obj")); // std::string("data/models/backpack/backpack.obj"));