EG/RenderPipelineFile/rpcore/shader/includes/gbuffer.inc.glsl
2025-07-24 11:37:46 +08:00

308 lines
11 KiB
GLSL

/**
*
* RenderPipeline
*
* Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#pragma once
#pragma include "includes/material.inc.glsl"
#pragma include "includes/normal_packing.inc.glsl"
#pragma include "includes/brdf.inc.glsl"
uniform mat4 p3d_ProjectionMatrix;
#if IN_GBUFFER_SHADER
/*
GBuffer Packing
*/
layout(location = 0) out vec4 gbuffer_out_0;
layout(location = 1) out vec4 gbuffer_out_1;
layout(location = 2) out vec4 gbuffer_out_2;
vec2 compute_velocity() {
// Compute velocity based on this and last frames mvp matrix
vec4 last_proj_pos = vOutput.last_proj_position;
vec2 last_texcoord = fma(last_proj_pos.xy / last_proj_pos.w, vec2(0.5), vec2(0.5));
vec2 curr_texcoord = gl_FragCoord.xy / SCREEN_SIZE;
return (curr_texcoord - last_texcoord);
}
// Lean mapping
float adjust_roughness(float roughness, float avg_normal_length) {
// Based on The Order : 1886 SIGGRAPH course notes implementation
if (avg_normal_length < 1.0)
{
float avg_len_sq = avg_normal_length * avg_normal_length;
float kappa = (3 * avg_normal_length - avg_normal_length * avg_len_sq) /
(1 - avg_len_sq);
float variance = 1.0 / (2.0 * kappa) ;
return sqrt(roughness * roughness + variance);
}
return roughness;
}
void render_material(MaterialShaderOutput m) {
// Compute material properties
vec3 normal = normalize(m.normal);
vec2 packed_normal = pack_normal_octahedron(normal);
vec2 velocity = compute_velocity();
// Clamp BaseColor, but only for negative values, we allow values > 1.0
// vec3 basecolor = pow(max(vec3(0), m.basecolor), vec3(2.2)) * 1.0;
vec3 basecolor = max(vec3(0), m.basecolor);
// Clamp properties like specular and metallic, which have to be in the
// 0 ... 1 range
float specular = clamp(m.specular_ior, 1.0001, 2.51);
float metallic = saturate(m.metallic);
float roughness = clamp(m.roughness, 0.03, 1.0);
roughness = adjust_roughness(roughness, length(m.normal));
// Pack all values to the gbuffer
gbuffer_out_0 = vec4(basecolor.r, basecolor.g, basecolor.b, roughness);
gbuffer_out_1 = vec4(packed_normal.x, packed_normal.y, metallic, specular);
gbuffer_out_2 = vec4(velocity.x, velocity.y, m.shading_model, m.shading_model_param0);
}
#else // IN_GBUFFER_SHADER
/*
GBuffer - Unpacking
*/
#pragma include "includes/transforms.inc.glsl"
// Common gbuffer data
struct GBufferData {
sampler2D Depth;
sampler2D Data0;
sampler2D Data1;
sampler2D Data2;
};
// Checks whether the given material is the skybox
bool is_skybox(vec3 pos, vec3 camera_pos) {
#if REFERENCE_MODE
return distance(pos, camera_pos) > 1000.0;
#endif
return distance(pos, camera_pos) > 20000.0;
}
bool is_skybox(Material m, vec3 camera_pos) {
return is_skybox(m.position, camera_pos);
}
bool is_skybox(Material m) { return is_skybox(m, MainSceneData.camera_pos); }
bool is_skybox(vec3 pos) { return is_skybox(pos, MainSceneData.camera_pos); }
// Returns the depth at a given texcoord
float get_gbuffer_depth(GBufferData data, vec2 coord) {
return textureLod(data.Depth, coord, 0).x;
}
// Returns the depth at a given texcoord
float get_gbuffer_depth(GBufferData data, ivec2 coord) {
return texelFetch(data.Depth, coord, 0).x;
}
// Returns the world space position at a given texcoord
vec3 get_gbuffer_position(GBufferData data, vec2 coord) {
float depth = get_gbuffer_depth(data, coord);
return calculate_surface_pos(depth, coord);
}
// Returns the world space normal at a given texcoord
vec3 get_gbuffer_normal(GBufferData data, vec2 coord) {
vec2 packed_normal = textureLod(data.Data1, coord, 0).xy;
return unpack_normal_octahedron(packed_normal);
}
// Returns the world space normal at a given texcoord
vec3 get_gbuffer_normal(GBufferData data, ivec2 coord) {
vec2 packed_normal = texelFetch(data.Data1, coord, 0).xy;
return unpack_normal_octahedron(packed_normal);
}
// Returns the velocity at a given coordinate
vec2 get_gbuffer_object_velocity(GBufferData data, vec2 coord) {
return textureLod(data.Data2, coord, 0).xy;
}
// Returns the velocity at a given coordinate
vec2 get_gbuffer_object_velocity(GBufferData data, ivec2 coord) {
return texelFetch(data.Data2, coord, 0).xy;
}
int get_gbuffer_shading_model(GBufferData data, vec2 coord) {
return int(textureLod(data.Data2, coord, 0).z);
}
float get_gbuffer_roughness(GBufferData data, vec2 coord) {
// XXX: take clearcoat into account
return square(clamp(textureLod(data.Data0, coord, 0).w, MINIMUM_ROUGHNESS, 1.0));
}
float get_gbuffer_roughness(GBufferData data, ivec2 coord) {
// XXX: take clearcoat into account
return square(clamp(texelFetch(data.Data0, coord, 0).w, MINIMUM_ROUGHNESS, 1.0));
}
// Unpacks a material from the gbuffer
Material unpack_material(GBufferData data, vec2 fcoord) {
// Fetch data from data-textures
vec4 data0 = textureLod(data.Data0, fcoord, 0);
vec4 data1 = textureLod(data.Data1, fcoord, 0);
vec4 data2 = textureLod(data.Data2, fcoord, 0);
Material m;
m.position = get_gbuffer_position(data, fcoord);
m.basecolor = data0.xyz;
m.linear_roughness = clamp(data0.w, MINIMUM_ROUGHNESS, 1.0);
m.roughness = m.linear_roughness * m.linear_roughness;
m.normal = unpack_normal_octahedron(data1.xy);
m.metallic = saturate(data1.z * 1.001 - 0.0005);
m.specular_ior = data1.w;
m.specular = ior_to_specular(data1.w);
m.shading_model = int(data2.z);
m.shading_model_param0 = data2.w;
// Velocity, not stored in the Material struct but stored in the G-Buffer
// vec2 velocity = data2.xy;
return m;
}
// Unpacks a material from the gbuffer
Material unpack_material(GBufferData data) {
vec2 fcoord = get_texcoord();
return unpack_material(data, fcoord);
}
#ifdef USE_GBUFFER_EXTENSIONS
/*
GBuffer extensions for reading gbuffer values without having to specify
the gbuffer.
*/
uniform GBufferData GBuffer;
// Returns the depth at a given texcoord
float get_depth_at(vec2 coord) {
return get_gbuffer_depth(GBuffer, coord);
}
// Returns the depth at a given texcoord
float get_depth_at(ivec2 coord) {
return get_gbuffer_depth(GBuffer, coord);
}
// Returns the view space position at a given texcoord
vec3 get_view_pos_at(vec2 coord) {
return calculate_view_pos(get_depth_at(coord), coord);
}
// Returns the world space position at a given texcoord
vec3 get_world_pos_at(vec2 coord) {
return calculate_surface_pos(get_depth_at(coord), coord);
}
// Returns the velocity given texcoord
vec2 get_object_velocity_at(vec2 coord) {
return get_gbuffer_object_velocity(GBuffer, coord);
}
// Returns the velocity given texcoord
vec2 get_object_velocity_at(ivec2 coord) {
return get_gbuffer_object_velocity(GBuffer, coord);
}
// Returns the view space normal at a given texcoord. This tries to find
// a good fit normal, but thus is quite expensive.
// It does not include normal mapping, since it uses the depth buffer as source.
vec3 get_view_normal(vec2 coord) {
// OPTIONAL: Just recover it from the world space normal.
// This has the advantage that it does include normal mapping.
#if 1
vec3 world_normal = get_gbuffer_normal(GBuffer, coord);
return world_normal_to_view(world_normal);
#endif
vec2 pixel_size = 1.0 / SCREEN_SIZE;
vec3 view_pos = get_view_pos_at(coord);
// Do some work to find a good view normal
vec3 dx_px = view_pos - get_view_pos_at(coord + pixel_size * vec2(1, 0));
vec3 dx_py = view_pos - get_view_pos_at(coord + pixel_size * vec2(0, 1));
vec3 dx_nx = get_view_pos_at(coord + pixel_size * vec2(-1, 0)) - view_pos;
vec3 dx_ny = get_view_pos_at(coord + pixel_size * vec2(0, -1)) - view_pos;
// Find the closest distance in depth
vec3 dx_x = abs(dx_px.z) < abs(dx_nx.z) ? vec3(dx_px) : vec3(dx_nx);
vec3 dx_y = abs(dx_py.z) < abs(dx_ny.z) ? vec3(dx_py) : vec3(dx_ny);
return normalize(cross(dx_x, dx_y));
}
// Returns the view space normal at a given texcoord. This approximates
// the normal instead of calculating it accurately, thus might produce
// smaller artifacts at edges. However you should prefer this method
// wherever possible. It does not include normal mapping, since it uses
// the depth buffer as source.
vec3 get_view_normal_approx(vec2 coord) {
vec3 view_pos = get_view_pos_at(coord);
vec2 pixel_size = 1.0 / SCREEN_SIZE;
vec3 dx_x = view_pos - get_view_pos_at(coord + pixel_size * vec2(1, 0));
vec3 dx_y = view_pos - get_view_pos_at(coord + pixel_size * vec2(0, 1));
return normalize(cross(dx_x, dx_y));
}
// Returns the cameras velocity
vec2 get_camera_velocity(vec2 texcoord) {
vec2 film_offset_bias = MainSceneData.current_film_offset *
vec2(1.0, 1.0 / ASPECT_RATIO);
vec3 pos = get_world_pos_at(texcoord - film_offset_bias);
vec4 last_proj = MainSceneData.last_view_proj_mat_no_jitter * vec4(pos, 1);
vec2 last_coord = fma(last_proj.xy / last_proj.w, vec2(0.5), vec2(0.5));
return last_coord - texcoord;
}
#endif
#endif // IN_GBUFFER_SHADER