Explorar o código

experimental ssao implementation

Kajetan Johannes Hammerle %!s(int64=5) %!d(string=hai) anos
pai
achega
34d2cd669e

+ 1 - 1
client/Client.cpp

@@ -47,7 +47,7 @@ void Client::tick()
     
     shader.storeCamera();
     
-    float factor = 2.0f;
+    float factor = 0.5f;
     if(keyManager.isDown(KEY_LEFT))
     {
         position.addMul(shader.getLeft(), factor);

+ 9 - 0
engine/Shader.cpp

@@ -38,6 +38,7 @@ void Shader::set3DMode(float lag)
     proj.set(3, 3, 0);   
     
     Engine::setMatrix(unifProjMatrix, proj.getValues());
+    Engine::testMat.set(proj);
     
     // -------------------------------------------------------------------------
     // calculate vectors for the view matrix
@@ -213,6 +214,14 @@ void Shader::setCamera(float x, float y, float z, float length, float width)
     widthAngle = width;
 }
 
+float Shader::distanceFromCamera(float x, float y, float z, float lag) const
+{
+    Vector3D cam = oldCamera;
+    cam.addMul(camera, lag);
+    cam.addMul(oldCamera, -lag);
+    return sqrtf((x - cam.getX()) * (x - cam.getX()) + (y - cam.getY()) * (y - cam.getY()) + (z - cam.getZ()) * (z - cam.getZ()));
+}
+
 bool Shader::isInFrustum(float x, float y, float z, float x2, float y2, float z2) const
 {
     //return true;

+ 2 - 0
engine/Shader.h

@@ -19,6 +19,8 @@ public:
     void storeCamera();
     void setCamera(float x, float y, float z, float length, float width);
     
+    float distanceFromCamera(float x, float y, float z, float lag) const;
+    
     bool isInFrustum(float x, float y, float z, float x2, float y2, float z2) const;
 
     const Vector3D& getFront() const;

+ 98 - 11
engine/Wrapper.cpp

@@ -1,4 +1,5 @@
 #include "Wrapper.h"
+#include <random>
 
 DummyClient DummyClient::dummy;
 
@@ -14,7 +15,12 @@ GLuint Engine::worldFrameBuffer = 0;
 GLuint Engine::worldPositionTexture = 0;
 GLuint Engine::worldNormalTexture = 0;
 GLuint Engine::worldColorTexture = 0;
-GLuint Engine::worldDepthRenderBuffer = 0;
+GLuint Engine::worldDepthTexture = 0;
+
+// ssao shader
+ShaderProgram Engine::ssaoShader;
+GLuint Engine::ssaoFrameBuffer = 0;
+GLuint Engine::ssaoTexture = 0;
 
 // post shader
 GLuint Engine::postVba = 0;
@@ -31,6 +37,11 @@ float Engine::testX = 0;
 float Engine::testY = 0;
 float Engine::testZ = 0;
 
+// ssao kernel data
+Vector3D Engine::ssaoKernel[ssaoKernelAmount];
+GLuint Engine::noiseTexture = 0;
+Matrix3D Engine::testMat;
+
 bool Engine::init(int width, int height, const char* name)
 {
     Engine::width = width;
@@ -76,6 +87,14 @@ bool Engine::init(int width, int height, const char* name)
     }
     activeProgram = worldShader.getProgram();
 
+    ssaoShader.compile("shader/ssaoVertex.vs", "shader/ssaoFragment.fs");
+    if(!ssaoShader.isValid())
+    {
+        glfwDestroyWindow(window);
+        glfwTerminate();
+        return false;
+    }
+    
     postShader.compile("shader/postVertex.vs", "shader/postFragment.fs");
     if(!postShader.isValid())
     {
@@ -265,6 +284,8 @@ void Engine::onInit()
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, NULL);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, worldPositionTexture, 0);   
     // world normal texture
     glGenTextures(1, &worldNormalTexture);
@@ -286,12 +307,13 @@ void Engine::onInit()
         GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 
     };
     glDrawBuffers(3, attachments);
-    // generate depth render buffer
-    glGenRenderbuffers(1, &worldDepthRenderBuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, worldDepthRenderBuffer);
-    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);  
-    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, worldDepthRenderBuffer);
-    glBindRenderbuffer(GL_RENDERBUFFER, 0);
+    // world depth texture
+    glGenTextures(1, &worldDepthTexture);
+    glBindTexture(GL_TEXTURE_2D, worldDepthTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, worldDepthTexture, 0); 
     // check if world framebuffer is okay
     if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
     {
@@ -323,6 +345,53 @@ void Engine::onInit()
     };
     
     glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 24, data, GL_STATIC_DRAW);
+    
+    // generate ssao kernel data
+    glGenFramebuffers(1, &ssaoFrameBuffer);
+    glBindFramebuffer(GL_FRAMEBUFFER, ssaoFrameBuffer);
+    // ssao color texture
+    glGenTextures(1, &ssaoTexture);
+    glBindTexture(GL_TEXTURE_2D, ssaoTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RGB, GL_FLOAT, NULL);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ssaoTexture, 0);  
+    // check if world framebuffer is okay
+    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+    {
+	cout << "ssao frame buffer is not complete!" << endl;
+    }
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    
+    std::uniform_real_distribution<float> randomF(0.0, 1.0); // random floats between 0.0 - 1.0
+    std::default_random_engine gen;
+    for(int i = 0; i < ssaoKernelAmount; i++)
+    {
+        ssaoKernel[i].set(randomF(gen) * 2.0 - 1.0, randomF(gen) * 2.0 - 1.0, randomF(gen));
+        ssaoKernel[i].normalize();
+        ssaoKernel[i].mul(randomF(gen));
+        
+        float scale = (float) i / ssaoKernelAmount; 
+        scale  = 0.1f + (scale * scale) * 0.9f;
+        ssaoKernel[i].mul(scale);
+        //cout << ssaoKernel[i] << endl;
+    }
+    
+    float noise[48];
+    for(int i = 0; i < 16; i++)
+    {
+        noise[i * 3] = randomF(gen) * 2.0 - 1.0;
+        noise[i * 3 + 1] = randomF(gen) * 2.0 - 1.0;
+        noise[i * 3 + 2] = 0.0f;
+    }  
+    
+    glGenTextures(1, &noiseTexture);
+    glBindTexture(GL_TEXTURE_2D, noiseTexture);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, 4, 4, 0, GL_RGB, GL_FLOAT, noise);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 
 }
 
 void Engine::onRenderTick(float lag)
@@ -358,18 +427,36 @@ void Engine::onRenderTick(float lag)
     glBindTexture(GL_TEXTURE_2D, worldNormalTexture);
     glActiveTexture(GL_TEXTURE3);
     glBindTexture(GL_TEXTURE_2D, worldColorTexture);
+    glActiveTexture(GL_TEXTURE4);
+    glBindTexture(GL_TEXTURE_2D, worldDepthTexture);
+    glActiveTexture(GL_TEXTURE5);
+    glBindTexture(GL_TEXTURE_2D, noiseTexture);
 
-    activeProgram = postShader.getProgram();
+    activeProgram = ssaoShader.getProgram();
     glUseProgram(activeProgram);
     
-    glUniform3f(glGetUniformLocation(activeProgram, "viewPos"), testX, testY, testZ);
+    for(int i = 0; i < ssaoKernelAmount; i++)
+    {
+        glUniform3f(glGetUniformLocation(activeProgram, (string("ssaoKernel[") + std::to_string(i) + "]").c_str()), 
+                ssaoKernel[i].getX(), ssaoKernel[i].getY(), ssaoKernel[i].getZ());
+    }
+    
+    Engine::setMatrix(glGetUniformLocation(activeProgram, "projMatrix"), testMat.getValues());
 
-    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glBindFramebuffer(GL_FRAMEBUFFER, ssaoFrameBuffer);
     glClear(GL_COLOR_BUFFER_BIT);
     glDisable(GL_DEPTH_TEST);
     glBindVertexArray(postVba);
     glBindBuffer(GL_ARRAY_BUFFER, postVbo);
     glDrawArrays(GL_TRIANGLES, 0, 6);
+    
+    glActiveTexture(GL_TEXTURE6);
+    glBindTexture(GL_TEXTURE_2D, ssaoTexture);
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    activeProgram = postShader.getProgram();
+    glUseProgram(activeProgram);
+    glDrawArrays(GL_TRIANGLES, 0, 6);
 }
 
 void Engine::onTerm()
@@ -381,7 +468,7 @@ void Engine::onTerm()
     glDeleteTextures(1, &worldPositionTexture);
     glDeleteTextures(1, &worldNormalTexture);
     glDeleteTextures(1, &worldColorTexture);
-    glDeleteRenderbuffers(1, &worldDepthRenderBuffer);
+    glDeleteTextures(1, &worldDepthTexture);
 }
 
 void Engine::setLineMode(bool mode)

+ 14 - 1
engine/Wrapper.h

@@ -4,6 +4,8 @@
 #include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include "ShaderProgram.h"
+#include "../math/Vector3D.h"
+#include "../math/Matrix3D.h"
 
 #include <iostream>
 
@@ -54,6 +56,7 @@ public:
     static float testX;
     static float testY;
     static float testZ;
+    static Matrix3D testMat;
 private:
     static const uint64_t NANOS_PER_TICK = 50000000;
     static const int MAX_TICKS_PER_FRAME = 5;
@@ -87,7 +90,12 @@ private:
     static GLuint worldPositionTexture;
     static GLuint worldNormalTexture;
     static GLuint worldColorTexture;
-    static GLuint worldDepthRenderBuffer;
+    static GLuint worldDepthTexture;
+    
+    // ssao shader
+    static ShaderProgram ssaoShader;
+    static GLuint ssaoFrameBuffer;
+    static GLuint ssaoTexture;
     
     // post shader
     static GLuint postVba;
@@ -99,6 +107,11 @@ private:
     static int height;
     
     static bool lineMode;
+    
+    // ssao kernel data
+    static constexpr int ssaoKernelAmount = 64;
+    static Vector3D ssaoKernel[ssaoKernelAmount];
+    static GLuint noiseTexture;
 };
 
 #endif

+ 18 - 24
shader/postFragment.fs

@@ -3,32 +3,26 @@
 layout (binding = 1) uniform sampler2D worldPositionSamp;
 layout (binding = 2) uniform sampler2D worldNormalSamp;
 layout (binding = 3) uniform sampler2D worldColorSamp;
+layout (binding = 4) uniform sampler2D worldDepthSamp;
+layout (binding = 5) uniform sampler2D noiseSamp;
+layout (binding = 6) uniform sampler2D ssaoSamp;
 
 in vec2 varTextureCoord;
 out vec4 color;
 
-vec3 lightPosition = vec3(16, 20, 16);
-vec3 lightColor = vec3(1, 1, 1);
-uniform vec3 viewPos;
-
 void main()
-{             
-    // retrieve data from G-buffer
-    vec3 FragPos = texture(worldPositionSamp, varTextureCoord).rgb;
-    vec3 Normal = texture(worldNormalSamp, varTextureCoord).rgb;
-    vec3 Albedo = vec3(1, 1, 1);//texture(gAlbedoSpec, TexCoords).rgb;
-    float Specular = 0.7;//texture(gAlbedoSpec, TexCoords).a;
-    
-    // then calculate lighting as usual
-    vec3 lighting = Albedo * 0.1; // hard-coded ambient component
-    vec3 viewDir = normalize(viewPos - FragPos);
-    //for(int i = 0; i < NR_LIGHTS; ++i)
-    //{
-        // diffuse
-        vec3 lightDir = normalize(lightPosition - FragPos);
-        vec3 diffuse = max(dot(Normal, lightDir), 0.0) * Albedo * lightColor;
-        lighting += diffuse;
-    //}
-    
-    color = vec4(lighting, 1.0) * texture(worldColorSamp, varTextureCoord);
-}  
+{
+    vec2 texelSize = 1.0 / vec2(textureSize(ssaoSamp, 0));
+    float result = 0.0;
+    const int radius = 2;
+    for(int x = -radius; x < radius; x++) 
+    {
+        for(int y = -radius; y < radius; y++) 
+        {
+            vec2 offset = vec2(float(x), float(y)) * texelSize;
+            result += texture(ssaoSamp, varTextureCoord + offset).r;
+        }
+    }
+    result /= (radius * radius * 4);
+    color = texture(worldColorSamp, varTextureCoord) * vec4(result, result, result, 1);
+}  

+ 54 - 0
shader/ssaoFragment.fs

@@ -0,0 +1,54 @@
+#version 430
+
+layout (binding = 1) uniform sampler2D worldPositionSamp;
+layout (binding = 2) uniform sampler2D worldNormalSamp;
+layout (binding = 3) uniform sampler2D worldColorSamp;
+layout (binding = 4) uniform sampler2D worldDepthSamp;
+layout (binding = 5) uniform sampler2D noiseSamp;
+
+const float bias = 0.025;
+const float radius = 0.5;
+const int kernelSize = 64;
+uniform vec3 ssaoKernel[kernelSize];
+uniform mat4 projMatrix;
+
+in vec2 varTextureCoord;
+out vec4 color;
+
+uniform vec3 viewPos;
+
+const vec2 noiseScale = vec2(1024.0 / 4.0, 620.0 / 4.0);
+
+void main()
+{     
+    vec3 fragPos = texture(worldPositionSamp, varTextureCoord).xyz;
+    vec3 normal = texture(worldNormalSamp, varTextureCoord).rgb;
+    vec3 randomVec = texture(noiseSamp, varTextureCoord * noiseScale).xyz; 
+
+    vec3 tangent = normalize(randomVec - normal * dot(randomVec, normal));
+    vec3 bitangent = cross(normal, tangent);
+    mat3 TBN = mat3(tangent, bitangent, normal);  
+
+    float occlusion = 0.0;
+    for(int i = 0; i < kernelSize; i++)
+    {
+        // get sample position
+        vec3 sampl = TBN * ssaoKernel[i]; // From tangent to view-space
+        sampl = fragPos + sampl * radius; 
+
+        vec4 offset = vec4(sampl, 1.0);
+        offset = projMatrix * offset;    // from view to clip-space
+        offset.xyz /= offset.w;               // perspective divide
+        offset.xyz = offset.xyz * 0.5 + 0.5; // transform to range 0.0 - 1.0  
+
+        float sampleDepth = texture(worldPositionSamp, offset.xy).z; 
+
+        //float rangeCheck = smoothstep(0.0, 1.0, radius / abs(fragPos.z - sampleDepth));
+        //occlusion += float(sampleDepth >= sampl.z + bias) * rangeCheck;   
+        occlusion += float(sampleDepth >= sampl.z + bias);  
+    }  
+    
+    occlusion /= kernelSize;
+    occlusion = 1 - occlusion;
+    color = vec4(occlusion, occlusion, occlusion, 1);
+} 

+ 12 - 0
shader/ssaoVertex.vs

@@ -0,0 +1,12 @@
+#version 430
+
+layout (location = 0) in vec2 pos;
+layout (location = 1) in vec2 tex;
+
+out vec2 varTextureCoord;
+
+void main(void)
+{ 
+    gl_Position = vec4(pos, 0.0, 1.0);
+    varTextureCoord = tex;
+}

+ 1 - 1
shader/worldFragment.fs

@@ -41,6 +41,6 @@ void main(void)
     }
 
     worldPosition = varPosition;
-    worldNormal = varNormal;
+    worldNormal = normalize(varNormal);
     worldColor = color;
 }

+ 7 - 4
shader/worldVertex.vs

@@ -22,7 +22,10 @@ out vec3 varNormal;
 
 void main(void)
 { 
-    varNormal = normal;
+    // model matrix should be used here ...
+    varNormal = (viewMatrix * vec4(normal, 1.0)).xyz;
+    //varNormal = (viewMatrix * modelMatrix * vec4(normal, 1.0)).xyz;
+    
     varTextureCoord = textureCoord; 
     varColor = vec4(color.xyz, 1);
 
@@ -38,7 +41,7 @@ void main(void)
         }
     }
     
-    vec4 worldPos = modelMatrix * vec4(position, 1.0);
-    varPosition = worldPos.xyz;
-    gl_Position = projMatrix * viewMatrix * worldPos;
+    vec4 viewPos = viewMatrix * modelMatrix * vec4(position, 1.0);
+    varPosition = viewPos.xyz;
+    gl_Position = projMatrix * viewPos;
 }

+ 1 - 0
world/Chunk.cpp

@@ -13,6 +13,7 @@ Chunk::Chunk(int chunkX, int chunkZ) : chunkX(chunkX), chunkZ(chunkZ)
         for(int x = 0; x < WIDTH; x++)
         {
             int maxY = (int) (sinf((x + chunkX * WIDTH) * 0.3) * 20 + 22) + (sinf((z + chunkZ * DEPTH) * 0.3) * 20 + 22);
+            //maxY = 10;
             if(maxY > HEIGHT)
             {
                 maxY = HEIGHT;