2

I've been following Thinmatrix tutorials to practice GLSL and Vector math, and now I'm trying to implement a PBR shader, that I borrowed from here(learnOpenGL). My first go made sure the models would not render. My old lambert shader works fine, but implementing the shader has been tricky. Otherwise I'm gonna have to port the c++ code block by block to java, which while I want to try over a period of time, I would like to implement a PBR Shader in my existing engine. I believe I've initiated the attributes:

@Override
    protected void bindAttributes() {
        super.bindAttribute(0, "aPos");
        super.bindAttribute(1, "aTexCoords");
        super.bindAttribute(2, "aNormal");
    }

but unsure how I should add the uniforms, at this point I'm taking a stab at it in the dark. I've noticed that the shader has a mat4 uniform view, mat4 uniform camPos and mat4 uniform model which I don't have binded in LWJGL (excuse my poor terminology). Currently my uniforms are:

@Override
protected void getAllUniformLocations() {
    location_transformationMatrix = super.getUniformLocation("transformationMatrix");
    location_projectionMatrix = super.getUniformLocation("projection");
    location_viewMatrix = super.getUniformLocation("view");
    location_shineDamper = super.getUniformLocation("shineDamper");
    location_reflectivity = super.getUniformLocation("reflectivity");
    location_useFakeLighting = super.getUniformLocation("useFakeLighting");
    location_skyColour = super.getUniformLocation("skyColour");
    location_numberOfRows = super.getUniformLocation("numberOfRows");
    location_offset = super.getUniformLocation("offset");
    location_lightPosition = new int[MAX_LIGHTS];
    location_lightColour = new int[MAX_LIGHTS];
    location_attenuation = new int [MAX_LIGHTS];
    for(int i=0;i<MAX_LIGHTS;i++) {
        location_lightPosition[i] = super.getUniformLocation("lightPositions[" + i + "]");
        location_lightColour[i] = super.getUniformLocation("lightColors[" + i + "]");
}

Vertex shader from learnOpengl:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
layout (location = 2) in vec3 aNormal;

out vec2 TexCoords;
out vec3 WorldPos;
out vec3 Normal;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

void main()
{
    TexCoords = aTexCoords;
    WorldPos = vec3(model * vec4(aPos, 1.0));
    Normal = mat3(model) * aNormal;   

    gl_Position =  projection * view * vec4(WorldPos, 1.0);
}

Fragment shader from learnopengl:

#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
in vec3 WorldPos;
in vec3 Normal;

// material parameters
uniform sampler2D albedoMap;
uniform sampler2D normalMap;
uniform sampler2D metallicMap;
uniform sampler2D roughnessMap;
uniform sampler2D aoMap;

// lights
uniform vec3 lightPositions[4];
uniform vec3 lightColors[4];

uniform vec3 camPos;

const float PI = 3.14159265359;
// ----------------------------------------------------------------------------
// Easy trick to get tangent-normals to world-space to keep PBR code simplified.
// Don't worry if you don't get what's going on; you generally want to do normal 
// mapping the usual way for performance anways; I do plan make a note of this 
// technique somewhere later in the normal mapping tutorial.
vec3 getNormalFromMap()
{
    vec3 tangentNormal = texture(normalMap, TexCoords).xyz * 2.0 - 1.0;

    vec3 Q1  = dFdx(WorldPos);
    vec3 Q2  = dFdy(WorldPos);
    vec2 st1 = dFdx(TexCoords);
    vec2 st2 = dFdy(TexCoords);

    vec3 N   = normalize(Normal);
    vec3 T  = normalize(Q1*st2.t - Q2*st1.t);
    vec3 B  = -normalize(cross(N, T));
    mat3 TBN = mat3(T, B, N);

    return normalize(TBN * tangentNormal);
}
// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
    float a = roughness*roughness;
    float a2 = a*a;
    float NdotH = max(dot(N, H), 0.0);
    float NdotH2 = NdotH*NdotH;

    float nom   = a2;
    float denom = (NdotH2 * (a2 - 1.0) + 1.0);
    denom = PI * denom * denom;

    return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness)
{
    float r = (roughness + 1.0);
    float k = (r*r) / 8.0;

    float nom   = NdotV;
    float denom = NdotV * (1.0 - k) + k;

    return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
    float NdotV = max(dot(N, V), 0.0);
    float NdotL = max(dot(N, L), 0.0);
    float ggx2 = GeometrySchlickGGX(NdotV, roughness);
    float ggx1 = GeometrySchlickGGX(NdotL, roughness);

    return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// ----------------------------------------------------------------------------
void main()
{       
    vec3 albedo     = pow(texture(albedoMap, TexCoords).rgb, vec3(2.2));
    float metallic  = texture(metallicMap, TexCoords).r;
    float roughness = texture(roughnessMap, TexCoords).r;
    float ao        = texture(aoMap, TexCoords).r;

    vec3 N = getNormalFromMap();
    vec3 V = normalize(camPos - WorldPos);

    // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 
    // of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow)    
    vec3 F0 = vec3(0.04); 
    F0 = mix(F0, albedo, metallic);

    // reflectance equation
    vec3 Lo = vec3(0.0);
    for(int i = 0; i < 4; ++i) 
    {
        // calculate per-light radiance
        vec3 L = normalize(lightPositions[i] - WorldPos);
        vec3 H = normalize(V + L);
        float distance = length(lightPositions[i] - WorldPos);
        float attenuation = 1.0 / (distance * distance);
        vec3 radiance = lightColors[i] * attenuation;

        // Cook-Torrance BRDF
        float NDF = DistributionGGX(N, H, roughness);   
        float G   = GeometrySmith(N, V, L, roughness);      
        vec3 F    = fresnelSchlick(max(dot(H, V), 0.0), F0);

        vec3 nominator    = NDF * G * F; 
        float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001; // 0.001 to prevent divide by zero.
        vec3 specular = nominator / denominator;

        // kS is equal to Fresnel
        vec3 kS = F;
        // for energy conservation, the diffuse and specular light can't
        // be above 1.0 (unless the surface emits light); to preserve this
        // relationship the diffuse component (kD) should equal 1.0 - kS.
        vec3 kD = vec3(1.0) - kS;
        // multiply kD by the inverse metalness such that only non-metals 
        // have diffuse lighting, or a linear blend if partly metal (pure metals
        // have no diffuse light).
        kD *= 1.0 - metallic;     

        // scale light by NdotL
        float NdotL = max(dot(N, L), 0.0);        

        // add to outgoing radiance Lo
        Lo += (kD * albedo / PI + specular) * radiance * NdotL;  // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
    }   

    // ambient lighting (note that the next IBL tutorial will replace 
    // this ambient lighting with environment lighting).
    vec3 ambient = vec3(0.03) * albedo * ao;

    vec3 color = ambient + Lo;

    // HDR tonemapping
    color = color / (color + vec3(1.0));
    // gamma correct
    color = pow(color, vec3(1.0/2.2)); 

    FragColor = vec4(color, 1.0);
}

I noticed that their are uniforms I'm not binding in lwjgl, some I'm unsure how to initiate or why it needs to be done (uniform mat4 model). I thought I could leave the uniforms from the thinmatrix tutorial and assumed that it wouldn't process. Please correct me if I'm wrong.

Any insight to the process behind making this will be much appreciated. I've been mostly watching talks and shadertoy based videos dealing with PBR.

heres my code based on thinmatrix tutorials: here

UPDATE: This is close, but now I'm trying out the textures and the albedo appears very dark and the others are indistinguishable. I also don't know what I'm supposed to do with the camPos uniform, or if its even neccesary.

enter image description here

lastest source code: here

update:

So the albedo seems to work but the ao, roughness and metallic seem to not want to bind. And its very dark.

In the renderer class that is supposed to help bind the textures.

 //Activate texture albedo texture 0
  GL13.glActiveTexture(GL13.GL_TEXTURE0);
  GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getAlbedo().getID());

  //Activate texture roughness texture 1
  GL13.glActiveTexture(GL13.GL_TEXTURE1);
  GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getRoughness().getID());

  //Activate texture metallic texture 2
  GL13.glActiveTexture(GL13.GL_TEXTURE2);
  GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getMetallic().getID());

  //Activate texture ao texture 0
  GL13.glActiveTexture(GL13.GL_TEXTURE3);
  GL11.glBindTexture(GL11.GL_TEXTURE_2D, model.getAo().getID());

enter image description here

UPDATE:

enter image description here

At the moment I've got the textures working. I feel like I can figure out the rest now. My only question would be: whats up with this huge texture seam? Is it because the texture maps im using are not seamless? I'm assuming it would go away if I make the normal, ao, rough and metallic seamless. The albedo seems seamless already, or perhaps not. Im being a bit nitpicky but just curious if that would help make it look better.

enter image description here

winnieTheWind
  • 141
  • 12
  • You need to get the uniform locations of all uniforms in your shaders. – Nick Clark Dec 02 '17 at 19:48
  • Ah yes! I managed to get the first step. I'm unsure what I'm supposed to do with the camPos variable. And now I'm trying to apply the the texture data to the Sampler2D for each texture map(albedo, roughness, metallic, ao). I have a TexturedModel class that can take in each image like so TexturedModel staticModel = new TexturedModel(model,albedo, roughness, metallic, ao); but the image is coming out dark and I can' tell if its registering each image to the model. – winnieTheWind Dec 11 '17 at 13:59
  • Use RenderDoc and make sure all of your textures are actually getting bound – Nick Clark Dec 11 '17 at 15:24
  • I'm unsure if I can use RenderDoc for java. I installed RenderDoc, and tried having it load a jar file to no avail. I tried a c++ example and that seemed to work. I've tried researching on debugging opengl with LWJGL and I didnt find much, but it seems like RenderDoc should work, which may be dependant on the version? as I have read somewhere on their website. Having a debugger seems like a good idea. Thanks for suggestion. – winnieTheWind Dec 12 '17 at 14:50
  • Try attaching it to the application after you have started running it. – Nick Clark Dec 12 '17 at 14:53
  • 'Connection Established to javaw [PID 10208] (No API detected)' is all I get. I've tried from the jar file, from an exe. I tried inject into process and attaching to running instance. Perhaps I may need to do more research and figure out the situation. – winnieTheWind Dec 12 '17 at 15:40
  • Perhaps it is the version of LWJGL im using. Im using LWJGL2. – winnieTheWind Dec 13 '17 at 04:16
  • That shouldn't make a difference. – Nick Clark Dec 13 '17 at 04:33
  • Well I'm currently using intelliJ. If I run the app from IntelliJ I can try to Inject into process, but it seems to not read the API. I've tried loading an executable built from JarSplice, which also didnt work. I tried the runnable jar but that didnt work. Im using RenderDoc 64 bit on a 64bit windows. There doesnt seem to be much info on using java applications with RenderDoc, which makes it all more confusing. Are there any IDE's you prefer to use that may make renderdoc work a bit better? – winnieTheWind Dec 13 '17 at 04:46
  • So I didn't manage to get RenderDoc working, however I managed to figure out how to bind the textures. What I didn't realize was that I had to bind the uniform of each map as an int. With the help of thinmatrix tutorials I managed to get that to work. Much appreciated Nick, you helped a little bit, lead me in the right direction. :) – winnieTheWind Dec 14 '17 at 11:48
  • No problem! Sorry I couldn't help more! ThinMatrix is a really good source, as it TheCherno on YouTube – Nick Clark Dec 14 '17 at 20:29

0 Answers0