Hey everyone,
I’ve been working on a PBR shader the last few months and have gotten a good ways with it I think. I’m just not sure if I have actually got a robust solution, and I’m getting PBR working with my terrain system and feel like the screenshot below is just a little to “blue” from IBL. You can see the left of the terrain has a blue tint compared to the right. I tend to believe that the camera is facing the directional light there… I don’t know. It just feels washed out to me, but might be correct.
Here’s the same PBR lighting on the helmet model (different shader code but same lighting functions)
Hoping some of you guys might have some tips or may see something not right in the code.
Here’s the full vert and frag code for the terrain.
//Vertex Shader
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shader_draw_parameters : enable
#extension GL_ARB_bindless_texture : enable
#define BUFFER_BINDING_CAMERA_SSBO 0
#define BUFFER_BINDING_ENTITY_SSBO 1
#define BUFFER_BINDING_MESH_SSBO 2
#define BUFFER_BINDING_MATERIAL_SSBO 3
#define BUFFER_BINDING_FRAMEBUFFER_SSBO 4
#define BUFFER_BINDING_LIGHTS_SSBO 5
#define BUFFER_BINDING_INSTANCE_SSBO 6
#define BUFFER_BINDING_VOXEL_OBJECT_SSBO 7
#define BUFFER_BINDING_PROBE_SSBO 8
//Shader master starts from 8. This needs to be manually changed if more enigne values are added here.
layout(location = 0) uniform int CameraID;
#ifdef DOUBLE_FLOAT
//layout(location = 1) uniform dvec3 WorldOffset;
#endif
struct Camera {
mat4 view;
mat4 projection;
};
layout(std430, binding = BUFFER_BINDING_CAMERA_SSBO) buffer CameraBuffer {
Camera cameras[];
};
mat4 CameraMatrix;
mat4 CameraInverseMatrix;
vec3 ExtractCameraPosition(in int CameraID) {
Camera cam = cameras[CameraID];
CameraInverseMatrix = inverse(cam.view);
return CameraInverseMatrix[3].xyz;
}
vec3 CameraPosition = ExtractCameraPosition(CameraID);
//Shoud only be used in a vertex shader due to : uint id = gl_BaseInstanceARB + gl_InstanceID;
struct Mesh {
uint entityID;
uint materialID;
};
layout(std430, binding = BUFFER_BINDING_MESH_SSBO) readonly buffer MeshBuffer {
Mesh instanceInfo[];
};
uint EntityID;
uint MaterialID;
uint ExtractInstanceInfo() {
uint id = gl_BaseInstanceARB + gl_InstanceID;
EntityID = instanceInfo[id].entityID;
MaterialID = instanceInfo[id].materialID;
return id;
}
uint MeshID = ExtractInstanceInfo();
//Entities
//Structs are auto padded to 16 bytes
struct Entity {
mat4 matrix;
mat4 normal_matrix;
vec4 color;
};
layout(std430, binding = BUFFER_BINDING_ENTITY_SSBO) readonly buffer EntityBuffer {
Entity entities[];
};
layout(location = 0) in vec3 in_vPosition;
layout(location = 1) in vec3 in_vNormal;
layout(location = 2) in vec3 in_vTangent;
layout(location = 3) in vec3 in_vBitangent;
layout(location = 4) in vec4 in_vCoords;
layout(location = 5) in vec3 in_vColor;
#define TEXTURE_DIFFUSE 0
#define TEXTURE_NORMAL 1
#define TEXTURE_TANGENT 2
#define TEXTURE_BITANGENT 3
#define TEXTURE_AO 4
#define TEXTURE_SLOT_ALBEDO 0
#define TEXTURE_SLOT_NORMAL 1
#define TEXTURE_SLOT_METALLIC_ROUGHNESS_AO 2
#define TEXTURE_SLOT_EMISSIVE 3
#define TEXTURE_SLOT_DISPLACMENT 4
struct Material {
vec4 diffuseColor;
float roughness;
float metallic;
float displacment_strength;
uvec2 textureHandle[16];
};
layout(std430, binding = 3) readonly buffer MaterialBlock { Material materials[]; };
layout(location = 0) out vec3 out_vNormal;
layout(location = 1) out vec4 out_vCoords;
layout(location = 2) out vec4 out_vColor;
layout(location = 3) flat out uint out_MaterialIndex;
layout(location = 4) flat out uint out_EntityIndex;
layout(location = 5) flat out uint out_MeshIndex;
layout(location = 6) out vec3 out_WorldPosition;
layout(location = 7) out vec3 out_CameraPosition;
layout(location = 8) out mat3 out_TBN;
void main() {
Entity entity = entities[EntityID];
Material material = materials[MaterialID];
vec3 vertex_position = in_vPosition;
//mat4 model_matrix = entity.matrix;
Camera camera = cameras[CameraID];
mat4 proj_matrix = camera.projection;
mat4 view_matrix = camera.view;
//Can we calculate the normal_matrix once per model?
//-mat3 normal_matrix = transpose(inverse(mat3(entity.matrix)));
mat3 normal_matrix = mat3(entity.matrix);
vec3 T = normalize(vec3(normal_matrix * vec3(1,0,0)));
vec3 B = normalize(vec3(normal_matrix * vec3(0,0,1)));
vec3 N = normalize(vec3(normal_matrix * vec3(0,1,0)));
out_TBN = mat3(T, B, N);
out_CameraPosition = inverse(view_matrix)[3].xyz;
out_WorldPosition = vec3(entity.matrix * vec4(vertex_position, 1.0f));
out_EntityIndex = EntityID;
//out_EntityColor = entity.color;
out_MeshIndex = MeshID;
out_MaterialIndex = MaterialID;
out_vNormal = N;
out_vColor = vec4(in_vColor, 1.0f);
out_vCoords = in_vCoords;
gl_Position = proj_matrix * view_matrix * vec4(out_WorldPosition, 1.0f);
}
//Fragment Shader
#version 460
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_bindless_texture : enable
layout(location = 0) in vec3 in_vNormal;
layout(location = 1) in vec4 in_vCoords;
layout(location = 2) in vec4 in_vColor;
layout(location = 3) flat in uint in_MaterialIndex;
layout(location = 4) flat in uint in_EntityIndex;
layout(location = 5) flat in uint in_MeshIndex;
layout(location = 6) in vec3 in_WorldPosition;
layout(location = 7) in vec3 in_CameraPosition;
layout(location = 8) in mat3 in_TBN;
#define TEXTURE_DIFFUSE 0
#define TEXTURE_NORMAL 1
#define TEXTURE_TANGENT 2
#define TEXTURE_BITANGENT 3
#define TEXTURE_AO 4
#define TEXTURE_SLOT_ALBEDO 0
#define TEXTURE_SLOT_NORMAL 1
#define TEXTURE_SLOT_METALLIC_ROUGHNESS_AO 2
#define TEXTURE_SLOT_EMISSIVE 3
#define TEXTURE_SLOT_DISPLACMENT 4
struct Material {
vec4 diffuseColor;
float roughness;
float metallic;
float displacment_strength;
uvec2 textureHandle[16];
};
layout(std430, binding = 3) readonly buffer MaterialBlock { Material materials[]; };
struct VoxelTerrainLayer {
uint material_index;
float min_height;
float max_height;
float min_slope;
float max_slope;
float min_angle;
float max_angle;
float transition;
};
float MacroVariation( in Material material , in int texture_slot , in vec2 coords , in float strength ) {
return mix( ( 1.0f - strength ) , 1.0f , ( ( texture( sampler2D( material.textureHandle[ texture_slot ] ) , ( coords * 0.2134f ) ).x + 0.5f ) * ( ( texture( sampler2D( material.textureHandle[ texture_slot ] ) , ( coords * 0.05341f ) ).x + 0.5f ) * ( texture( sampler2D( material.textureHandle[ texture_slot ] ) , ( coords * 0.002f ) ).x + 0.5f ) ) ) );
}
vec4 SampleDiffuse( Material material , in vec2 coords ) {
return ( material.diffuseColor * ( texture( sampler2D( material.textureHandle[ 0 ] ) , coords ) * MacroVariation( material , 0 , coords , 1.0f ) ) );
}
vec3 SampleNormal( in Material material , in vec2 coords ) {
vec4 node22_pixel = texture( sampler2D( material.textureHandle[ 1 ] ) , coords );
return vec3( ( ( node22_pixel.x * 2.0f ) - 1.0f ) , ( ( node22_pixel.z * 2.0f ) - 1.0f ) , ( ( node22_pixel.y * 2.0f ) - 1.0f ) );
}
void SampleLayer( in VoxelTerrainLayer layer , in vec2 coords , in float slope , out vec3 normal , out vec4 color , out float blend ) {
normal = SampleNormal(materials[ layer.material_index ] , coords);
blend = smoothstep( ( layer.max_slope + 0.001f ) , layer.min_slope , slope );
if ( blend > 0.0f ) {
color = ( materials[ layer.material_index ].diffuseColor * SampleDiffuse(materials[ layer.material_index ] , coords) );
}
}
#define BUFFER_BINDING_CAMERA_SSBO 0
#define BUFFER_BINDING_ENTITY_SSBO 1
#define BUFFER_BINDING_MESH_SSBO 2
#define BUFFER_BINDING_MATERIAL_SSBO 3
#define BUFFER_BINDING_FRAMEBUFFER_SSBO 4
#define BUFFER_BINDING_LIGHTS_SSBO 5
#define BUFFER_BINDING_INSTANCE_SSBO 6
#define BUFFER_BINDING_VOXEL_OBJECT_SSBO 7
#define BUFFER_BINDING_PROBE_SSBO 8
//Shader master starts from 8. This needs to be manually changed if more enigne values are added here.
layout(location = 0) uniform int CameraID;
#ifdef DOUBLE_FLOAT
layout(location = 1) uniform dvec3 WorldOffset;
#endif
struct Camera {
mat4 view;
mat4 projection;
};
layout(std430, binding = BUFFER_BINDING_CAMERA_SSBO) buffer CameraBuffer {
Camera cameras[];
};
mat4 CameraMatrix;
mat4 CameraInverseMatrix;
vec3 ExtractCameraPosition(in int CameraID) {
Camera cam = cameras[CameraID];
CameraInverseMatrix = inverse(cam.view);
return CameraInverseMatrix[3].xyz;
}
vec3 CameraPosition = ExtractCameraPosition(CameraID);
vec2 DistanceCoords( in vec2 coords , in vec3 world_position , in float uv_scale , in float step_size ) {
return ( coords / ( uv_scale * ( floor( ( distance(world_position, CameraPosition) / step_size ) ) + 1.0f ) ) );
}
uniform float object_size = 1.0f;
uniform int total_material_layers = 0;
void VoxelTerrainInput( out float size , out float height , out float detail , out float noise , out float ao , out vec3 normal , out vec3 tangent , out vec3 bi_tangent , out vec2 object_uv , out vec2 tile_uv , out int layer_count , out uint layers ) {
size = object_size;
vec4 node23_tex_sample = texture( sampler2D( materials[ in_MaterialIndex ].textureHandle[ TEXTURE_DIFFUSE ] ) , ( in_WorldPosition.xz / object_size ) );
height = node23_tex_sample.x;
detail = node23_tex_sample.y;
noise = node23_tex_sample.z;
ao = node23_tex_sample.w;
normal = SampleNormal(materials[ in_MaterialIndex ] , ( in_WorldPosition.xz / object_size ));
tangent = texture( sampler2D( materials[ in_MaterialIndex ].textureHandle[ TEXTURE_TANGENT ] ) , ( in_WorldPosition.xz / object_size ) ).xyz;
bi_tangent = texture( sampler2D( materials[ in_MaterialIndex ].textureHandle[ TEXTURE_BITANGENT ] ) , ( in_WorldPosition.xz / object_size ) ).xyz;
object_uv = ( in_WorldPosition.xz / object_size );
tile_uv = in_WorldPosition.xz;
layer_count = total_material_layers;
}
float GetSlope( in vec3 normal , in vec3 up ) {
return clamp(dot( normal , up ), 0.0f, 1.0f);
}
const int node38_array_size = 32;
layout(std140, binding = 8) uniform material_layers_block {
VoxelTerrainLayer material_layers[32];
};
out vec4 FragColor;
void main() {
float node29_size;
float node29_height;
float node29_detail;
float node29_noise;
float node29_ao;
vec3 node29_normal;
vec3 node29_tangent;
vec3 node29_bi_tangent;
vec2 node29_object_uv;
vec2 node29_tile_uv;
int node29_layer_count;
uint node29_layers;
VoxelTerrainInput( node29_size , node29_height , node29_detail , node29_noise , node29_ao , node29_normal , node29_tangent , node29_bi_tangent , node29_object_uv , node29_tile_uv , node29_layer_count , node29_layers );
vec4 node13_variable = vec4( 1.0f, 1.0f, 1.0f, 0.0f );
vec3 node6_normal;
vec4 node6_color;
float node6_blend;
for( int node35_index = 0; node35_index < node29_layer_count; node35_index++ ) {
SampleLayer( material_layers[node35_index] , DistanceCoords( node29_tile_uv , in_WorldPosition , 1.0f , 32.0f ) , GetSlope(node29_normal , vec3( 0.0f, 1.0f, 0.0f )) , node6_normal , node6_color , node6_blend );
if ( node35_index == 0 ) {
node6_blend = 1.0f;
}
node13_variable = mix( node13_variable , node6_color , node6_blend );
}
FragColor = node13_variable;
}



