post processing for path traced images

This commit is contained in:
Tuomas Katajisto 2025-07-30 22:26:56 +03:00
parent e36908096a
commit d0ee73035b
16 changed files with 2894 additions and 3445 deletions

Binary file not shown.

Binary file not shown.

View File

@ -11,6 +11,7 @@ Sky_Config :: struct {
sunLightColor : Vector3 = .{1.0, 1.0, 1.0};
sunPosition : Vector3 = #run normalize(Vector3.{0.5, 0.5, 0.5});
sunIntensity : float = 1.0;
skyIntensity : float = 10.0;
}
Trixel_Data :: struct {

View File

@ -1,701 +0,0 @@
#version 460
#extension GL_EXT_scalar_block_layout : require
#extension GL_GOOGLE_include_directive : require
#include "../common.h"
#extension GL_EXT_ray_query : require
precision highp float;
const float PI = 3.14159265359;
layout(local_size_x = WORKGROUP_WIDTH, local_size_y = WORKGROUP_HEIGHT, local_size_z = 1) in;
layout(binding = BINDING_IMAGEDATA, set = 0, scalar) buffer storageBuffer
{
vec3 imageData[];
};
layout(binding = BINDING_TLAS, set = 0) uniform accelerationStructureEXT tlas;
layout(binding = BINDING_VERTICES, set = 0, scalar) buffer Vertices
{
vec3 vertices[];
};
layout(binding = BINDING_INDICES, set = 0, scalar) buffer Indices
{
uint indices[];
};
layout(binding = BINDING_COLORS, set = 0, scalar) buffer Color
{
vec4 colors[];
};
layout(binding = BINDING_ORIGINS, set = 0) buffer LayoutPixels
{
LayoutPixel layoutbuf[];
};
layout(binding = BINDING_DEPTH, set = 0, scalar) buffer Depths
{
vec3 depthData[];
};
layout(push_constant) uniform PushConsts
{
PushConstants pushConstants;
};
// GGX
float ggx (vec3 N, vec3 V, vec3 L, float roughness, float F0) {
float alpha = roughness*roughness;
vec3 H = normalize(L - V);
float dotLH = max(0.0, dot(L,H));
float dotNH = max(0.0, dot(N,H));
float dotNL = max(0.0, dot(N,L));
float alphaSqr = alpha * alpha;
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
float D = alphaSqr / (3.141592653589793 * denom * denom);
float F = F0 + (1.0 - F0) * pow(1.0 - dotLH, 5.0);
float k = 0.5 * alpha;
float k2 = k * k;
return dotNL * D * F / (dotLH*dotLH*(1.0-k2)+k2);
}
vec3 fresnelSchlick(float cosTheta, vec3 F0) {
// This clamp is different than in the exercise 7 instructions
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 a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return num / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / 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;
}
float GGXPDF(vec3 wo, vec3 wi, vec3 normal, float roughness) {
return (GeometrySchlickGGX(dot(wi, normal), roughness) * GeometrySchlickGGX(dot(wo, normal), roughness)) / GeometrySchlickGGX(dot(wi, normal), roughness);
}
vec3 SampleVndf_GGX(vec2 u, vec3 wi, float alpha, vec3 n)
{
// decompose the vector in parallel and perpendicular components
vec3 wi_z = n * dot(wi, n);
vec3 wi_xy = wi - wi_z;
// warp to the hemisphere configuration
vec3 wiStd = normalize(wi_z - alpha * wi_xy);
// sample a spherical cap in (-wiStd.z, 1]
float wiStd_z = dot(wiStd, n);
float phi = (2.0f * u.x - 1.0f) * PI;
float z = (1.0f - u.y) * (1.0f + wiStd_z) - wiStd_z;
float sinTheta = sqrt(clamp(1.0f - z * z, 0.0f, 1.0f));
float x = sinTheta * cos(phi);
float y = sinTheta * sin(phi);
vec3 cStd = vec3(x, y, z);
// reflect sample to align with normal
vec3 up = vec3(0, 0, 1);
vec3 wr = n + up;
float wrz_safe = max(wr.z, 1e-6);
vec3 c = dot(wr, cStd) * wr / wrz_safe - cStd;
// compute halfway direction as standard normal
vec3 wmStd = c + wiStd;
vec3 wmStd_z = n * dot(n, wmStd);
vec3 wmStd_xy = wmStd_z - wmStd;
// warp back to the ellipsoid configuration
vec3 wm = normalize(wmStd_z + alpha * wmStd_xy);
// return final normal
return wm;
}
float pdf_vndf_isotropic(vec3 wo, vec3 wi, float alpha, vec3 n)
{
float alphaSquare = alpha * alpha;
vec3 wm = normalize(wo + wi);
float zm = dot(wm, n);
float zi = dot(wi, n);
float nrm = inversesqrt((zi * zi) * (1.0f - alphaSquare) + alphaSquare);
float sigmaStd = (zi * nrm) * 0.5f + 0.5f;
float sigmaI = sigmaStd / nrm;
float nrmN = (zm * zm) * (alphaSquare - 1.0f) + 1.0f;
return alphaSquare / (PI * 4.0f * nrmN * nrmN * sigmaI);
}
// ----- SKY SHADER -------
const float time = 0.0;
const float cirrus = 0.5;
const float cumulus = 0.6;
vec4 skyBase = vec4(0.3843, 0.8117, 0.9568, 1.0); //vec4(0.3843, 0.8117, 0.9568, 1.0);
vec4 skyTop = vec4(0.17, 0.4, 0.95, 1.0); //vec4(0.17, 0.4, 0.95, 1.0);
vec4 sunDisk = vec4(1.0, 1.0, 1.0, 1.0); //vec4(1.0, 1.0, 1.0, 1.0);
vec4 horizonHalo = vec4(1.0, 1.0, 1.0, 1.0); //vec4(1.0, 1.0, 1.0, 1.0);
vec4 sunHalo = vec4(1.0, 1.0, 1.0, 1.0); //vec4(1.0, 1.0, 1.0, 1.0);
float hash(float n)
{
return fract(sin(n) * 43758.5453123);
}
float noise(vec3 x)
{
vec3 f = fract(x);
float n = dot(floor(x), vec3(1.0, 157.0, 113.0));
return mix(mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 157.0), hash(n + 158.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 270.0), hash(n + 271.0), f.x), f.y), f.z);
}
const mat3 m = mat3(0.0, 1.60, 1.20, -1.6, 0.72, -0.96, -1.2, -0.96, 1.28);
float fbm(vec3 p)
{
float f = 0.0;
f += noise(p) / 2.0; p = m * p * 1.1;
f += noise(p) / 4.0; p = m * p * 1.2;
f += noise(p) / 6.0; p = m * p * 1.3;
f += noise(p) / 12.0; p = m * p * 1.4;
f += noise(p) / 24.0;
return f;
}
vec3 sky(vec3 skypos, vec3 sunpos) {
vec3 sunCol = sunDisk.xyz;
vec3 baseSky = skyBase.xyz;
vec3 topSky = skyTop.xyz;
float sDist = dot(normalize(skypos), normalize(sunpos));
vec3 npos = normalize(skypos);
vec3 skyGradient = mix(baseSky, topSky, clamp(skypos.y * 2.0, 0.0, 0.7));
vec3 final = skyGradient;
final += sunHalo.xyz * clamp((sDist - 0.95) * 10.0, 0.0, 0.8) * 0.2;
// Sun disk
if(sDist > 0.9999) {
final = sunDisk.xyz;
}
// Horizon halo
final += mix(horizonHalo.xyz, vec3(0.0,0.0,0.0), clamp(abs(npos.y) * 80.0, 0.0, 1.0)) * 0.1;
final = vec3(final);
// Cirrus Clouds
float density = smoothstep(1.0 - cirrus, 1.0, fbm(npos.xyz / npos.y * 2.0 + time * 0.05)) * 0.3;
final.rgb = mix(final.rgb, vec3(1.0, 1.0, 1.0), max(0.0, npos.y) * density * 2.0);
final += noise(skypos * 1000.0) * 0.02;
return final;
}
// --- END SKY ----
// Random number generation using pcg32i_random_t, using inc = 1. Our random state is a uint.
uint stepRNG(uint rngState)
{
return rngState * 747796405 + 1;
}
// Steps the RNG and returns a floating-point value between 0 and 1 inclusive.
float stepAndOutputRNGFloat(inout uint rngState)
{
// Condensed version of pcg_output_rxs_m_xs_32_32, with simple conversion to floating-point [0,1].
rngState = stepRNG(rngState);
uint word = ((rngState >> ((rngState >> 28) + 4)) ^ rngState) * 277803737;
word = (word >> 22) ^ word;
return float(word) / 4294967295.0f;
}
// Returns the color of the sky in a given direction (in linear color space)
vec3 skyColor(vec3 direction)
{
// +y in world space is up, so:
if(direction.y > 0.0f)
{
return mix(vec3(1.0f), vec3(0.25f, 0.5f, 1.0f), direction.y);
}
else
{
return vec3(0.03f);
}
}
vec3 randomSpherePoint(vec3 rand) {
float ang1 = (rand.x + 1.0) * PI; // [-1..1) -> [0..2*PI)
float u = rand.y; // [-1..1), cos and acos(2v-1) cancel each other out, so we arrive at [-1..1)
float u2 = u * u;
float sqrt1MinusU2 = sqrt(1.0 - u2);
float x = sqrt1MinusU2 * cos(ang1);
float y = sqrt1MinusU2 * sin(ang1);
float z = u;
return vec3(x, y, z);
}
vec3 randomHemispherePoint(vec3 rand, vec3 n) {
vec3 v = randomSpherePoint(rand);
vec3 v2 = v * sign(dot(v, n));
if(length(v2) < 0.0001) {
return n;
}
return v2;
}
struct HitInfo
{
vec3 color;
vec3 worldPosition;
vec3 worldNormal;
vec3 emission;
int isWater;
float metallic;
float roughness;
};
HitInfo getObjectHitInfo(rayQueryEXT rayQuery)
{
HitInfo result;
// Get the ID of the triangle
const int primitiveID = rayQueryGetIntersectionPrimitiveIndexEXT(rayQuery, true);
uint offset = rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT(rayQuery, true);
int colorOffset = rayQueryGetIntersectionInstanceCustomIndexEXT(rayQuery, true);
// Get the indices of the vertices of the triangle
const uint i0 = indices[offset + 3 * primitiveID + 0];
const uint i1 = indices[offset + 3 * primitiveID + 1];
const uint i2 = indices[offset + 3 * primitiveID + 2];
// Get the vertices of the triangle
const vec3 v0 = vertices[i0];
const vec3 v1 = vertices[i1];
const vec3 v2 = vertices[i2];
// Get the barycentric coordinates of the intersection
vec3 barycentrics = vec3(0.0, rayQueryGetIntersectionBarycentricsEXT(rayQuery, true));
barycentrics.x = 1.0 - barycentrics.y - barycentrics.z;
// Compute the coordinates of the intersection
const vec3 objectPos = v0 * barycentrics.x + v1 * barycentrics.y + v2 * barycentrics.z;
const mat4x3 objectToWorld = rayQueryGetIntersectionObjectToWorldEXT(rayQuery, true);
result.worldPosition = objectToWorld * vec4(objectPos, 1.0f);
const vec3 objectNormal = cross(v1 - v0, v2 - v0);
// Transform normals from object space to world space. These use the transpose of the inverse matrix,
// because they're directions of normals, not positions:
const mat4x3 objectToWorldInverse = rayQueryGetIntersectionWorldToObjectEXT(rayQuery, true);
result.worldNormal = normalize((objectNormal * objectToWorldInverse).xyz);
result.emission = vec3(0);
if(colorOffset == 121212) {
result.color = vec3(0.9, 0.9, 1.0);
result.isWater = 1;
return result;
}
// Calculate position inside the model, and use it to get the color for the trixel...
//result.color = vec3(objectPos.x, objectPos.y, objectPos.z);
const vec3 objectTexturePos = objectPos - objectNormal * 0.005;
int cx = int(objectTexturePos.z * 16);
int cy = int(objectTexturePos.y * 16);
int cz = int(objectTexturePos.x * 16);
if(cx > 15) cx = 15;
if(cy > 15) cy = 15;
if(cz > 15) cz = 15;
vec4 color = colors[colorOffset + cx + cy * 16 + cz * 16 * 16];
result.color = color.xyz;
// result.color = vec3(0.5, 0.2, 0.6);
int packedMaterial = int(color.w * 255.0);
float emittance = float((packedMaterial >> 1) & 0x3) / 3.0;
result.roughness = max(float((packedMaterial >> 5) & 0x7) / 7.0, 0.01);
result.metallic = float((packedMaterial >> 3) & 0x3) / 3.0;
bool roughFlag = (packedMaterial & 0x1) != 0;
if(emittance > 0) {
result.color = vec3(1.0, 1.0, 1.0);
result.emission = result.color * 50.0 * emittance;
}
// result.color = vec3(float(cx) / 15.0, float(cy) / 15.0, 0.0);
// result.color = vec3(result.worldNormal);
result.isWater = 0;
const float dotX = dot(result.worldNormal, vec3(0.0, 1.0, 0.0));
return result;
}
vec3 wave(vec4 wave, vec3 p, inout vec3 tangent, inout vec3 binormal) {
float steepness = wave.z;
float wavelength = wave.w;
float k = 2.0 * 3.141 / wavelength;
float c = 2.0;
vec2 d = normalize(vec2(wave.x, wave.y));
float f = k * (dot(d, p.xz) - c * (0.0 * 0.3));
float a = steepness / k;
tangent += vec3(
-d.x * d.x * (steepness * sin(f)),
d.x * (steepness * cos(f)),
-d.x * d.y * (steepness * sin(f))
);
binormal += vec3(
-d.x * d.y * (steepness * sin(f)),
d.y * (steepness * cos(f)),
-d.y * d.y * (steepness * sin(f))
);
return vec3(
d.x * (a * cos(f)),
a * sin(f),
d.y * (a * cos(f))
);
}
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
{
float a = roughness*roughness;
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
// from spherical coordinates to cartesian coordinates
vec3 H;
H.x = cos(phi) * sinTheta;
H.y = sin(phi) * sinTheta;
H.z = cosTheta;
// from tangent-space vector to world-space sample vector
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, N));
vec3 bitangent = cross(N, tangent);
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
return normalize(sampleVec);
}
vec3 BRDF_spec_ggx(vec3 normal, vec3 incoming, vec3 outgoing, vec3 color, float metallic, float roughness) {
vec3 N = normal;
vec3 L = outgoing;
vec3 V = incoming;
vec3 H = normalize(V + L);
vec3 F0 = vec3(0.04);
F0 = mix(F0, color, metallic);
vec3 F = fresnelSchlick(max(dot(H,V), 0.0), F0);
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;
vec3 specular = numerator / denominator;
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
return specular;
}
vec3 BRDF_spec(vec3 normal, vec3 incoming, vec3 outgoing, vec3 color, float metallic, float roughness) {
vec3 N = normal;
vec3 L = outgoing;
vec3 V = incoming;
vec3 H = normalize(V + L);
vec3 F0 = vec3(0.04);
F0 = mix(F0, color, metallic);
vec3 F = fresnelSchlick(max(dot(H,V), 0.0), F0);
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;
vec3 specular = F / denominator;
return specular;
}
vec3 BRDF_diff(vec3 normal, vec3 incoming, vec3 outgoing, vec3 color, float metallic, float roughness) {
vec3 N = normal;
vec3 L = outgoing;
vec3 V = incoming;
vec3 H = normalize(V + L);
vec3 F0 = vec3(0.04);
F0 = mix(F0, color, metallic);
vec3 F = fresnelSchlick(max(dot(H,V), 0.0), F0);
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
return (kD * color / PI);
}
vec3 BRDF(vec3 normal, vec3 incoming, vec3 outgoing, vec3 color, float metallic, float roughness) {
vec3 N = normal;
vec3 L = outgoing;
vec3 V = incoming;
vec3 H = normalize(V + L);
vec3 F0 = vec3(0.04);
F0 = mix(F0, color, metallic);
vec3 F = fresnelSchlick(max(dot(H,V), 0.0), F0);
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;
vec3 specular = numerator / denominator;
vec3 kD = vec3(1.0) - F;
kD *= 1.0 - metallic;
return (kD * color / PI + specular);
}
vec3 filmic_aces(vec3 v)
{
v = v * mat3(
0.59719f, 0.35458f, 0.04823f,
0.07600f, 0.90834f, 0.01566f,
0.02840f, 0.13383f, 0.83777f
);
return (v * (v + 0.0245786f) - 9.0537e-5f) /
(v * (0.983729f * v + 0.4329510f) + 0.238081f) * mat3(
1.60475f, -0.53108f, -0.07367f,
-0.10208f, 1.10813f, -0.00605f,
-0.00327f, -0.07276f, 1.07602f
);
}
vec3 ImportanceSampleCosine(vec2 Xi, vec3 N) {
// Create basis from normal
vec3 B1, B2;
if (abs(N.x) < abs(N.y)) {
B1 = normalize(cross(N, vec3(1.0, 0.0, 0.0)));
} else {
B1 = normalize(cross(N, vec3(0.0, 1.0, 0.0)));
}
B2 = cross(N, B1);
// Cosine weighted sampling
float phi = 2.0 * PI * Xi.x;
float cosTheta = sqrt(Xi.y);
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
// Convert to cartesian coordinates
vec3 H;
H.x = cos(phi) * sinTheta;
H.y = sin(phi) * sinTheta;
H.z = cosTheta;
// Transform from local space to world space
return normalize(H.x * B1 + H.y * B2 + H.z * N);
}
void main()
{
const uvec2 resolution = uvec2(pushConstants.render_width, pushConstants.render_height);
const uvec2 pixel = gl_GlobalInvocationID.xy;
if((pixel.x >= resolution.x) || (pixel.y >= resolution.y)) {
return;
}
uint linearIndex = resolution.x * pixel.y + pixel.x;
uint rngState = (pushConstants.sample_batch * resolution.y + pixel.y) * 3 * (pushConstants.sample_batch + 20) * resolution.x + pixel.x;
const vec3 cameraOrigin = vec3(5.0, 5.0, 5.0);
const vec3 lookAt = vec3(2.5, 0.0, 2.5);
const vec3 generalDirection = normalize(lookAt - cameraOrigin);
const vec3 up = vec3(0.0, 1.0, 0.0);
const vec3 right = cross(generalDirection, up);
const float fovVerticalSlope = 1.0 / 5.0;
highp vec3 summedPixelColor = vec3(0.0);
const int NUM_SAMPLES = 10;
float depth = 99999999.0;
for(int sampleIdx = 0; sampleIdx < NUM_SAMPLES; sampleIdx++) {
// vec3 rayOrigin = cameraOrigin;
// const vec2 randomPixelCenter = vec2(pixel) + vec2(stepAndOutputRNGFloat(rngState), stepAndOutputRNGFloat(rngState));
// const vec2 screenUV = vec2(2.0 * (float(randomPixelCenter.x) + 0.5 - 0.5 * resolution.x) / resolution.y, //
// -(2.0 * (float(randomPixelCenter.y) + 0.5 - 0.5 * resolution.y) / resolution.y) // Flip the y axis
// );
// vec3 rayDirection = vec3(generalDirection + right * screenUV.x + up * screenUV.y);
// rayDirection = normalize(rayDirection);
vec2 Xi;
Xi.x = stepAndOutputRNGFloat(rngState);
Xi.y = stepAndOutputRNGFloat(rngState);
float rayDirectionArr[3] = layoutbuf[linearIndex].direction;
float rayOriginArr[3] = layoutbuf[linearIndex].origin;
float rayRoughness = layoutbuf[linearIndex].roughness;
vec3 rayDirection = vec3(rayDirectionArr[0], rayDirectionArr[1], rayDirectionArr[2]);
vec3 rayOrigin = vec3(rayOriginArr[0], rayOriginArr[1], rayOriginArr[2]);
if(rayRoughness < 0.98) {
vec3 H = ImportanceSampleGGX(Xi, rayDirection, max(0.01, rayRoughness));
vec3 L = normalize(2.0 * dot(rayDirection, H) * H - rayDirection);
rayDirection = L;
} else {
vec3 randV;
randV.x = stepAndOutputRNGFloat(rngState);
randV.y = stepAndOutputRNGFloat(rngState);
randV.z = stepAndOutputRNGFloat(rngState);
randV = normalize(randV);
rayDirection = randomHemispherePoint(randV, rayDirection);
}
vec3 attenuation = vec3(1.0);
// vec3 sunDir = normalize(vec3(0.7, 0.6, 0.6));
vec3 sunDir = normalize(vec3(pushConstants.sunPosition.x, pushConstants.sunPosition.y, pushConstants.sunPosition.z));
// vec3 sunDir = normalize(vec3(0.3, 0.6, 0.4));
vec3 randV;
randV.x = stepAndOutputRNGFloat(rngState);
randV.y = stepAndOutputRNGFloat(rngState);
randV.z = stepAndOutputRNGFloat(rngState);
randV = normalize(randV);
float regularization = 1.0;
for(int tracedSegments = 0; tracedSegments < 12; tracedSegments++) {
float raySelector = stepAndOutputRNGFloat(rngState);
rayQueryEXT rayQuery;
rayQueryInitializeEXT(rayQuery, tlas, gl_RayFlagsOpaqueEXT, 0xFF, rayOrigin, 0.0, rayDirection, 10000.0);
while(rayQueryProceedEXT(rayQuery)) {}
if(rayQueryGetIntersectionTypeEXT(rayQuery, true) == gl_RayQueryCommittedIntersectionTriangleEXT) {
HitInfo hitInfo = getObjectHitInfo(rayQuery);
if(tracedSegments == 0) {
depth = min(depth, length(hitInfo.worldPosition - rayOrigin));
if(depth < 0.01) {
break;
}
}
if(hitInfo.isWater == 1) {
// vec3 waterTangent = vec3(1.0, 0.0, 0.0);
// vec3 waterBinormal = vec3(0.0, 0.0, 1.0);
vec3 p = vec3(0.0);
// p += wave(vec4(1.0, 0.0, 0.06, 1.0), hitInfo.worldPosition, waterTangent, waterBinormal);
// p += wave(vec4(1.0, 1.0, 0.04, 0.3), hitInfo.worldPosition, waterTangent, waterBinormal);
// p += wave(vec4(1.0, 0.5, 0.04, 0.2), hitInfo.worldPosition, waterTangent, waterBinormal);
// vec3 waterNormal = normalize(cross(normalize(waterBinormal), normalize(waterTangent)));
hitInfo.worldNormal = faceforward(hitInfo.worldNormal, rayDirection, hitInfo.worldNormal);
rayOrigin = hitInfo.worldPosition + 0.01 * hitInfo.worldNormal;
vec3 oldDirection = rayDirection;
rayDirection = normalize(reflect(rayDirection, hitInfo.worldNormal));
rayQueryInitializeEXT(rayQuery, tlas, gl_RayFlagsOpaqueEXT, 0xFF, rayOrigin, 0.0, normalize(sunDir), 10000.0);
while(rayQueryProceedEXT(rayQuery)) {}
// if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionTriangleEXT) {
// summedPixelColor += max(dot(normalize(sunDir - oldDirection), waterNormal), 0.0) * attenuation * 0.3;
// }
attenuation *= hitInfo.color;
} else {
// Add emission:
summedPixelColor += attenuation * hitInfo.emission;
hitInfo.worldNormal = faceforward(hitInfo.worldNormal, rayDirection, hitInfo.worldNormal);
rayOrigin = hitInfo.worldPosition + 0.001 * hitInfo.worldNormal;
vec3 randV;
randV.x = stepAndOutputRNGFloat(rngState);
randV.y = stepAndOutputRNGFloat(rngState);
randV.z = stepAndOutputRNGFloat(rngState);
randV = normalize(randV);
vec3 oldDirection = rayDirection;
if(raySelector > 0.5) { // DIFFUSE
// Cast ray towards sun to check sun situation:
rayDirection = randomHemispherePoint(randV, hitInfo.worldNormal);
rayDirection = normalize(rayDirection);
float regularizationGamma = 0.5;
float bsdf_pdf = (1.0 / (PI * 2.0)) * 0.5;
if(bsdf_pdf != 0.0f) regularization *= max(1 - regularizationGamma / pow(bsdf_pdf, 0.25f), 0.0f);
hitInfo.roughness = 1.0f - ((1.0f - hitInfo.roughness) * regularization);
attenuation *= abs(dot(rayDirection, hitInfo.worldNormal));
attenuation *= BRDF_diff(hitInfo.worldNormal, -oldDirection, rayDirection, hitInfo.color, hitInfo.metallic, hitInfo.roughness);
attenuation /= bsdf_pdf;
rayQueryInitializeEXT(rayQuery, tlas, gl_RayFlagsOpaqueEXT, 0xFF, rayOrigin, 0.0, normalize(sunDir), 10000.0);
while(rayQueryProceedEXT(rayQuery)) {}
if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionTriangleEXT) {
summedPixelColor += attenuation * BRDF_diff(hitInfo.worldNormal, -oldDirection, sunDir, hitInfo.color, hitInfo.metallic, hitInfo.roughness) * vec3(pushConstants.sunIntensity) * max(0.0, dot(sunDir, hitInfo.worldNormal));
}
} else { // SPECULAR
if(hitInfo.roughness > 0.01) {
vec3 microfacetNormal = SampleVndf_GGX(vec2(randV.x, randV.y), -oldDirection, hitInfo.roughness, hitInfo.worldNormal);
rayDirection = reflect(oldDirection, microfacetNormal);
rayDirection = normalize(rayDirection);
float regularizationGamma = 0.5;
float bsdf_pdf = pdf_vndf_isotropic(rayDirection, -oldDirection, hitInfo.roughness, hitInfo.worldNormal) * 0.5;
if(bsdf_pdf != 0.0f) regularization *= max(1 - regularizationGamma / pow(bsdf_pdf, 0.25f), 0.0f);
hitInfo.roughness = 1.0f - ((1.0f - hitInfo.roughness) * regularization);
attenuation *= abs(dot(rayDirection, hitInfo.worldNormal));
attenuation *= BRDF_spec_ggx(hitInfo.worldNormal, -oldDirection, rayDirection, hitInfo.color, hitInfo.metallic, hitInfo.roughness);
attenuation /= bsdf_pdf;
} else {
vec3 microfacetNormal = hitInfo.worldNormal;
rayDirection = reflect(oldDirection, microfacetNormal);
rayDirection = normalize(rayDirection);
float regularizationGamma = 0.5;
hitInfo.roughness = 1.0f - ((1.0f - hitInfo.roughness) * regularization);
attenuation *= abs(dot(rayDirection, hitInfo.worldNormal));
attenuation *= BRDF_spec(hitInfo.worldNormal, -oldDirection, rayDirection, hitInfo.color, hitInfo.metallic, hitInfo.roughness);
attenuation /= 0.5;
}
rayQueryInitializeEXT(rayQuery, tlas, gl_RayFlagsOpaqueEXT, 0xFF, rayOrigin, 0.0, normalize(sunDir), 10000.0);
while(rayQueryProceedEXT(rayQuery)) {}
if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionTriangleEXT) {
summedPixelColor += attenuation * BRDF_spec_ggx(hitInfo.worldNormal, -oldDirection, sunDir, hitInfo.color, hitInfo.metallic, hitInfo.roughness) * vec3(pushConstants.sunIntensity) * max(0.0, dot(sunDir, hitInfo.worldNormal));
}
}
}
} else {
summedPixelColor += sky(rayDirection, sunDir) * attenuation;
// summedPixelColor += rayDirection * attenuation;
break;
}
}
}
// Get the index of this invocation in the buffer:
// Blend with the averaged image in the buffer:
vec3 averagePixelColor = vec3(summedPixelColor / float(NUM_SAMPLES));
if(any(isnan(averagePixelColor))) {
averagePixelColor = vec3(0.0, 0.0, 0.0);
}
if(pushConstants.sample_batch != 0)
{
averagePixelColor = (pushConstants.sample_batch * imageData[linearIndex] + vec3(averagePixelColor)) / (pushConstants.sample_batch + 1);
}
if(pushConstants.sample_batch == pushConstants.max_batch - 1) {
imageData[linearIndex] = averagePixelColor;
depthData[linearIndex] = vec3(depth, 0.0, 0.0);
} else {
imageData[linearIndex] = averagePixelColor;
}
}

Binary file not shown.

View File

@ -18,9 +18,11 @@ oldCameraRotation : float;
oldCameraTilt : float;
cameraCenter : Vector2;
tacomaSamples : s32 = 100;
tacomaWidth : s32 = 500;
tacomaHeight : s32 = 500;
tacomaSamples : s32 = 100;
tacomaResolution : s32 = 500;
tacomaExposure : float = 1.0;
tacomaContrast : float = 1.0;
tacomaSaturation : float = 1.0;
lastInputTime : float64;
@ -152,7 +154,7 @@ draw_tacoma_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
#if HAS_TACOMA {
if GR.button(r, "Render with Tacoma", *theme.button_theme) {
cam := get_level_editor_camera();
test_gen(tacomaWidth, tacomaHeight, .{cam.target, cam.position, tacomaSamples, false});
test_gen(tacomaResolution, tacomaResolution, .{tacomaExposure, tacomaContrast, tacomaSaturation}, .{cam.target, cam.position, tacomaSamples, true}, world.conf);
}
r.y += r.h;
if current_screenshot.valid {
@ -169,15 +171,23 @@ draw_tacoma_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
GR.label(r, "Samples", *t_label_left(theme));
r.y += r.h;
GR.slider(r, *tacomaSamples, 10, 100000, 10, *theme.slider_theme);
GR.slider(r, *tacomaSamples, 10, 10000, 10, *theme.slider_theme);
r.y += r.h;
GR.label(r, "Width", *t_label_left(theme));
GR.label(r, "Resolution", *t_label_left(theme));
r.y += r.h;
GR.slider(r, *tacomaWidth, 10, 5000, 10, *theme.slider_theme);
GR.slider(r, *tacomaResolution, 10, 5000, 10, *theme.slider_theme);
r.y += r.h;
GR.label(r, "Height", *t_label_left(theme));
GR.label(r, "Exposure", *t_label_left(theme));
r.y += r.h;
GR.slider(r, *tacomaHeight, 10, 5000, 10, *theme.slider_theme);
GR.slider(r, *tacomaExposure, 0.5, 3.0, 0.1, *theme.slider_theme);
r.y += r.h;
GR.label(r, "Contrast", *t_label_left(theme));
r.y += r.h;
GR.slider(r, *tacomaContrast, 0.5, 3.0, 0.1, *theme.slider_theme);
r.y += r.h;
GR.label(r, "Saturation", *t_label_left(theme));
r.y += r.h;
GR.slider(r, *tacomaSaturation, 0.5, 3.0, 0.1, *theme.slider_theme);
r.y += r.h;

View File

@ -14,11 +14,40 @@ Tacoma_Screenshot :: struct {
current_screenshot : Tacoma_Screenshot;
Post_Process :: struct {
exposure : float;
contrast : float;
saturation : float;
}
test_gen :: (w: s32, h: s32, conf: Tacoma.Gen_Config) {
post_process_pipeline :: (color: Vector3, post_process: Post_Process) -> Vector3 {
vec3 :: (f: float) -> Vector3 {
return .{f,f,f};
}
v := color;
v *= pow(2.0, post_process.exposure);
v *= 0.6;
a : float = 2.51;
b : float = 0.03;
c : float = 2.43;
d : float = 0.59;
e : float = 0.14;
pre_clamp := (v*(a*v+vec3(b)))/(v*(c*v+vec3(d))+vec3(e));
sdr : Vector3;
sdr.x = clamp(pre_clamp.x, 0.0, 1.0);
sdr.y = clamp(pre_clamp.y, 0.0, 1.0);
sdr.z = clamp(pre_clamp.z, 0.0, 1.0);
sdr = vec3(0.5) + post_process.contrast * (sdr - vec3(0.5));
LUMINANCE := Vector3.{0.2126, 0.7152, 0.0722};
grayscale := dot(sdr, LUMINANCE);
return lerp(vec3(grayscale), sdr, post_process.saturation);
}
test_gen :: (w: s32, h: s32, postprocess: Post_Process, conf: Tacoma.Gen_Config, worldConf: World_Config) {
trile := get_trile("test");
print("Testing gen!\n");
ttrile : Tacoma.Trile_Data;
for x: 0..15 {
for y: 0..15 {
@ -31,6 +60,7 @@ test_gen :: (w: s32, h: s32, conf: Tacoma.Gen_Config) {
}
}
}
gfx := get_trile_gfx("test");
ttrile.vertices = gfx.vertices.data;
ttrile.vertexCount = cast(s32) (gfx.vertices.count / 3);
@ -40,20 +70,52 @@ test_gen :: (w: s32, h: s32, conf: Tacoma.Gen_Config) {
sky : Tacoma.Sky_Config;
sky.skyBase = worldConf.skyBase;
sky.skyTop = worldConf.skyTop;
sky.sunDisk = worldConf.sunDisk;
sky.horizonHalo = worldConf.horizonHalo;
sky.sunHalo = worldConf.sunHalo;
sky.sunLightColor = worldConf.sunLightColor;
sky.sunPosition = worldConf.sunPosition;
sky.sunIntensity = worldConf.sunIntensity;
sky.skyIntensity = worldConf.skyIntensity;
ts : Tacoma.Trile_Set = .{trile_list.data, trile_list.count};
wTrile : Tacoma.World_Trile = .{
0,
.{0.0, 0.0, 0.0}
.{1.0, 0.0, 0.0}
};
wTrile2 : Tacoma.World_Trile = .{
0,
.{0.0, 1.0, 1.0}
};
world : Tacoma.World = .{*wTrile, 1};
triles : [2]Tacoma.World_Trile = .[wTrile, wTrile2];
world : Tacoma.World = .{triles.data, 2};
ptr := Tacoma.do_gen("./modules/Tacoma/", w, h, sky, ts, world, conf);
data := cast(*float) talloc(w*h*4*size_of(float));
memcpy(data, ptr, w*h*4*4);
for 0..(w*h) {
color : Vector3;
color.x = data[it * 4 + 0];
color.y = data[it * 4 + 1];
color.z = data[it * 4 + 2];
color = post_process_pipeline(color, postprocess);
data[it * 4 + 0] = color.x;
data[it * 4 + 1] = color.y;
data[it * 4 + 2] = color.z;
}
imgdata : sg_image_data;
imgdata.subimage[0][0] = .{ptr, cast(u64) (w*h*4*4)};
imgdata.subimage[0][0] = .{data, cast(u64) (w*h*4*4)};
texdesc : sg_image_desc = .{
render_target = false,
width = w,

View File

@ -168,7 +168,7 @@ create_trile_pipeline :: () {
pipeline.layout.attrs[ATTR_trile_position] = .{ format = .FLOAT3, buffer_index = 0 };
pipeline.layout.attrs[ATTR_trile_normal] = .{ format = .FLOAT3, buffer_index = 1 };
pipeline.layout.attrs[ATTR_trile_centre] = .{ format = .FLOAT3, buffer_index = 2 };
pipeline.layout.attrs[ATTR_trile_instance] = .{ format = .MAT4, buffer_index = 3 };
pipeline.layout.attrs[ATTR_trile_instance] = .{ format = .FLOAT4, buffer_index = 3 };
pipeline.depth = .{
write_enabled = true,
compare = .LESS_EQUAL,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,7 @@ layout(binding=1) uniform sky_world_config {
vec3 sunLightColor;
vec3 sunPosition;
float sunIntensity;
float skyIntensity;
int hasClouds;

View File

@ -12,7 +12,8 @@ layout(binding=0) uniform trile_vs_params {
out vec3 cam;
out vec3 to_center;
out vec3 vpos;
out vec3 vpos; // The actual position;
out vec3 ipos; // Trile space position;
out vec4 fnormal;
void main() {
@ -20,6 +21,7 @@ void main() {
fnormal = normal;
to_center = centre.xyz - position.xyz;
vpos = position.xyz + instance.xyz;
ipos = position.xyz;
cam = camera;
}
@end
@ -35,6 +37,7 @@ layout(binding=1) uniform trile_world_config {
vec3 sunLightColor;
vec3 sunPosition;
float sunIntensity;
float skyIntensity;
int hasClouds;
@ -48,6 +51,7 @@ layout(binding=1) uniform trile_world_config {
in vec3 cam;
in vec3 to_center;
in vec3 vpos;
in vec3 ipos;
in vec4 fnormal;
out vec4 frag_color;
@ -174,7 +178,7 @@ vec3 fresnelSchlick(float cosTheta, vec3 F0) {
void main() {
//frag_color = vec4((fnormal.xyz + vec3(1.0, 1.0, 1.0)) * 0.5, 1.0);
vec3 pos_after_adjust = vpos - fnormal.xyz * 0.02;
vec3 pos_after_adjust = ipos - fnormal.xyz * 0.02;
int count = 0;
vec4 trixel_material;
while (count < 5) {

View File

@ -36,6 +36,7 @@ layout(binding=1) uniform trixel_world_config {
vec3 sunLightColor;
vec3 sunPosition;
float sunIntensity;
float skyIntensity;
int hasClouds;

View File

@ -7,7 +7,8 @@ World_Config :: struct {
sunHalo : Vector3 = .{1.0, 1.0, 1.0}; @Color
sunLightColor : Vector3 = .{1.0, 1.0, 1.0}; @Color
sunPosition : Vector3 = #run normalize(Vector3.{0.2, 0.3, 0.4});
sunIntensity : float = 2.0; @Slider,0,10,0.5
sunIntensity : float = 2.0; @Slider,0,100,0.5
skyIntensity : float = 2.0; @Slider,0,10,0.5
hasClouds : s32 = 1; @Slider,0,1,1