339 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			GLSL
		
	
	
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			GLSL
		
	
	
	
	
	
| /*
 | |
| Copyright (c) 2024 Turánszki János
 | |
| 
 | |
| 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.
 | |
|  */
 | |
| #ifndef _CONETRACE_GLSL_
 | |
| #define _CONETRACE_GLSL_
 | |
| 
 | |
| #include "std/voxels_constants.glsl"
 | |
| 
 | |
| // References
 | |
| // https://github.com/Friduric/voxel-cone-tracing
 | |
| // https://github.com/Cigg/Voxel-Cone-Tracing
 | |
| // https://github.com/GreatBlambo/voxel_cone_tracing/
 | |
| // http://simonstechblog.blogspot.com/2013/01/implementing-voxel-cone-tracing.html
 | |
| // http://leifnode.com/2015/05/voxel-cone-traced-global-illumination/
 | |
| // http://www.seas.upenn.edu/%7Epcozzi/OpenGLInsights/OpenGLInsights-SparseVoxelization.pdf
 | |
| // https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf
 | |
| 
 | |
| const float MAX_DISTANCE = voxelgiRange;
 | |
| 
 | |
| #ifdef _VoxelGI
 | |
| uniform sampler3D dummy;
 | |
| 
 | |
| vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
 | |
|  	vec4 col = vec4(0.0);
 | |
| 	vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
 | |
| 	vec3 half_texel = vec3(0.5) / voxelgiResolution;
 | |
| 	tc = tc * 0.5 + 0.5;
 | |
| 	tc = clamp(tc, half_texel, 1.0 - half_texel);
 | |
| 	tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
 | |
| 	tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
 | |
| 
 | |
| 	if (precomputed_direction == 0) {
 | |
| 		col = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0)
 | |
| 			+ direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0)
 | |
| 			+ direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0);
 | |
| 	}
 | |
| 	else
 | |
| 		col = textureLod(voxels, tc, 0);
 | |
| 
 | |
| 	col *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
 | |
| 
 | |
| 	return col;
 | |
| }
 | |
| 
 | |
| #endif
 | |
| #ifdef _VoxelAOvar
 | |
| float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
 | |
|  	float opac = 0.0;
 | |
| 	vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
 | |
| 	vec3 half_texel = vec3(0.5) / voxelgiResolution;
 | |
| 	tc = tc * 0.5 + 0.5;
 | |
| 	tc = clamp(tc, half_texel, 1.0 - half_texel);
 | |
| 	tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
 | |
| 	tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
 | |
| 
 | |
| 	if (precomputed_direction == 0) {
 | |
| 		opac = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0).r
 | |
| 			+ direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0).r
 | |
| 			+ direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0).r;
 | |
| 	}
 | |
| 	else
 | |
| 		opac = textureLod(voxels, tc, 0).r;
 | |
| 
 | |
| 	opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
 | |
| 
 | |
| 	return opac;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef _VoxelGI
 | |
| vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const bool use_sdf, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
 | |
|     vec4 sampleCol = vec4(0.0);
 | |
| 	float voxelSize0 = float(clipmaps[0]) * 2.0;
 | |
| 	float dist = voxelSize0;
 | |
| 	float step_dist = dist;
 | |
| 	vec3 samplePos;
 | |
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
 | |
| 	int clipmap_index0 = 0;
 | |
| 
 | |
| 	vec3 aniso_direction = -dir;
 | |
| 	vec3 face_offset = vec3(
 | |
| 		aniso_direction.x > 0.0 ? 0 : 1,
 | |
| 		aniso_direction.y > 0.0 ? 2 : 3,
 | |
| 		aniso_direction.z > 0.0 ? 4 : 5
 | |
| 	) / (6 + DIFFUSE_CONE_COUNT);
 | |
| 	vec3 direction_weight = abs(dir);
 | |
| 
 | |
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5);
 | |
| 
 | |
|     while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
 | |
| 		vec4 mipSample = vec4(0.0);
 | |
| 		float diam = max(voxelSize0, dist * coneCoefficient);
 | |
|         float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
 | |
|         float clipmap_index = floor(lod);
 | |
| 		float clipmap_blend = fract(lod);
 | |
| 		vec3 p0 = start_pos + dir * dist;
 | |
| 
 | |
|         samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
 | |
| 		samplePos = samplePos * 0.5 + 0.5;
 | |
| 
 | |
| 		if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) {
 | |
| 			clipmap_index0++;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
 | |
| 
 | |
| 		if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
 | |
| 			vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
 | |
| 			mipSample = mix(mipSample, mipSampleNext, smoothstep(0.0, 1.0, clipmap_blend));
 | |
| 		}
 | |
| 
 | |
| 		sampleCol += (1.0 - sampleCol.a) * mipSample;
 | |
| 
 | |
| 		float stepSizeCurrent = step_size;
 | |
| 		if (use_sdf) {
 | |
| 			// half texel correction is applied to avoid sampling over current clipmap:
 | |
| 			const vec3 half_texel = vec3(0.5) / voxelgiResolution;
 | |
| 			vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
 | |
| 			tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
 | |
| 			float sdf = textureLod(voxelsSDF, tc0, 0).r;
 | |
| 			stepSizeCurrent = max(step_size, sdf - diam);
 | |
| 		}
 | |
| 		step_dist = diam * stepSizeCurrent;
 | |
| 		dist += step_dist;
 | |
| 	}
 | |
|     return sampleCol;
 | |
| }
 | |
| 
 | |
| vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
 | |
| 	float sum = 0.0;
 | |
| 	vec4 amount = vec4(0.0);
 | |
| 	for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) {
 | |
| 		vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
 | |
| 		const float cosTheta = dot(normal, coneDir);
 | |
| 		if (cosTheta <= 0)
 | |
| 			continue;
 | |
| 		int precomputed_direction = 6 + i;
 | |
| 		amount += traceCone(voxels, dummy, origin, normal, coneDir, precomputed_direction, false, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
 | |
| 		sum += cosTheta;
 | |
| 	}
 | |
| 
 | |
| 	amount /= sum;
 | |
| 	amount.rgb = max(amount.rgb, vec3(0.0));
 | |
| 	amount.a = clamp(amount.a, 0.0, 1.0);
 | |
| 
 | |
| 	return amount * voxelgiOcc;
 | |
| }
 | |
| 
 | |
| vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 viewDir, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
 | |
| 	vec3 specularDir = reflect(-viewDir, normal);
 | |
| 	vec3 P = origin + specularDir * ((BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5)) * voxelgiStep;
 | |
| 	vec4 amount = traceCone(voxels, voxelsSDF, P, normal, specularDir, 0, true, roughness, voxelgiStep, clipmaps);
 | |
| 
 | |
| 	amount.rgb = max(vec3(0.0), amount.rgb);
 | |
| 	amount.a = clamp(amount.a, 0.0, 1.0);
 | |
| 
 | |
| 	return amount * voxelgiOcc;
 | |
| }
 | |
| 
 | |
| vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
 | |
|  	const float transmittance = 1.0;
 | |
|  	vec3 refractionDir = refract(-viewDir, normal, 1.0 / ior);
 | |
|  	vec3 P = origin + refractionDir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep;
 | |
| 	vec4 amount =  transmittance * traceCone(voxels, voxelsSDF, P, normal, refractionDir, 0, true, roughness, voxelgiStep, clipmaps);
 | |
| 
 | |
| 	amount.rgb = max(vec3(0.0), amount.rgb);
 | |
| 	amount.a = clamp(amount.a, 0.0, 1.0);
 | |
| 
 | |
| 	return amount * voxelgiOcc;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef _VoxelAOvar
 | |
| float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
 | |
| 	float sampleCol = 0.0;
 | |
| 	float voxelSize0 = float(clipmaps[0]) * 2.0;
 | |
| 	float dist = voxelSize0;
 | |
| 	float step_dist = dist;
 | |
| 	vec3 samplePos;
 | |
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
 | |
| 	int clipmap_index0 = 0;
 | |
| 
 | |
| 	vec3 aniso_direction = -dir;
 | |
| 	vec3 face_offset = vec3(
 | |
| 		aniso_direction.x > 0.0 ? 0 : 1,
 | |
| 		aniso_direction.y > 0.0 ? 2 : 3,
 | |
| 		aniso_direction.z > 0.0 ? 4 : 5
 | |
| 	) / (6 + DIFFUSE_CONE_COUNT);
 | |
| 	vec3 direction_weight = abs(dir);
 | |
| 
 | |
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5);
 | |
| 
 | |
|     while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
 | |
| 		float mipSample = 0.0;
 | |
| 		float diam = max(voxelSize0, dist * coneCoefficient);
 | |
|         float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
 | |
| 		float clipmap_index = floor(lod);
 | |
| 		float clipmap_blend = fract(lod);
 | |
| 		vec3 p0 = start_pos + dir * dist;
 | |
| 
 | |
|         samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
 | |
| 		samplePos = samplePos * 0.5 + 0.5;
 | |
| 
 | |
| 		if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) {
 | |
| 			clipmap_index0++;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
 | |
| 
 | |
| 		if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
 | |
| 			float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
 | |
| 			mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
 | |
| 		}
 | |
| 
 | |
| 		sampleCol += (1.0 - sampleCol) * mipSample;
 | |
| 
 | |
| 		step_dist = diam * step_size;
 | |
| 		dist += step_dist;
 | |
| 	}
 | |
|     return sampleCol;
 | |
| }
 | |
| 
 | |
| 
 | |
| float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
 | |
| 	float sum = 0.0;
 | |
| 	float amount = 0.0;
 | |
| 	for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) {
 | |
| 		vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
 | |
| 		int precomputed_direction = 6 + i;
 | |
| 		const float cosTheta = dot(normal, coneDir);
 | |
| 		if (cosTheta <= 0)
 | |
| 			continue;
 | |
| 		amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
 | |
| 		sum += cosTheta;
 | |
| 	}
 | |
| 	amount /= sum;
 | |
| 	amount = clamp(amount, 0.0, 1.0);
 | |
| 	return amount * voxelgiOcc;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #ifdef _VoxelShadow
 | |
| float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
 | |
|     float sampleCol = 0.0;
 | |
| 	float voxelSize0 = float(clipmaps[0]) * 2.0;
 | |
| 	float dist = voxelSize0;
 | |
| 	float step_dist = dist;
 | |
| 	vec3 samplePos;
 | |
| 	vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
 | |
| 	int clipmap_index0 = 0;
 | |
| 
 | |
| 	vec3 aniso_direction = -dir;
 | |
| 	vec3 face_offset = vec3(
 | |
| 		aniso_direction.x > 0.0 ? 0 : 1,
 | |
| 		aniso_direction.y > 0.0 ? 2 : 3,
 | |
| 		aniso_direction.z > 0.0 ? 4 : 5
 | |
| 	) / (6 + DIFFUSE_CONE_COUNT);
 | |
| 	vec3 direction_weight = abs(dir);
 | |
| 	float coneCoefficient = 2.0 * tan(aperture * 0.5);
 | |
| 
 | |
|     while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
 | |
| 		float mipSample = 0.0;
 | |
| 		float diam = max(voxelSize0, dist * coneCoefficient);
 | |
|         float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
 | |
| 		float clipmap_index = floor(lod);
 | |
| 		float clipmap_blend = fract(lod);
 | |
| 		vec3 p0 = start_pos + dir * dist;
 | |
| 
 | |
|         samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
 | |
| 		samplePos = samplePos * 0.5 + 0.5;
 | |
| 
 | |
| 		if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) {
 | |
| 			clipmap_index0++;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		#ifdef _VoxelAOvar
 | |
| 		mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight);
 | |
| 		#else
 | |
| 		mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a;
 | |
| 		#endif
 | |
| 
 | |
| 		if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
 | |
| 			#ifdef _VoxelAOvar
 | |
| 			float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight);
 | |
| 			#else
 | |
| 			float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a;
 | |
| 			#endif
 | |
| 			mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
 | |
| 		}
 | |
| 
 | |
| 		sampleCol += (1.0 - sampleCol) * mipSample;
 | |
| 
 | |
| 		float stepSizeCurrent = step_size;
 | |
| 
 | |
| 		// half texel correction is applied to avoid sampling over current clipmap:
 | |
| 		const vec3 half_texel = vec3(0.5) / voxelgiResolution;
 | |
| 		vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
 | |
| 		tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
 | |
| 		float sdf = textureLod(voxelsSDF, tc0, 0.0).r;
 | |
| 		stepSizeCurrent = max(step_size, sdf - diam);
 | |
| 
 | |
| 		step_dist = diam * stepSizeCurrent;
 | |
| 		dist += step_dist;
 | |
| 	}
 | |
| 	return sampleCol;
 | |
| }
 | |
| 
 | |
| 
 | |
| float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel) {
 | |
|  	vec3 P = origin + dir * (BayerMatrix8[int(pixel.x) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep;
 | |
| 	float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps);
 | |
| 	amount = clamp(amount, 0.0, 1.0);
 | |
| 	return amount * voxelgiOcc;
 | |
| }
 | |
| #endif
 | |
| #endif // _CONETRACE_GLSL_
 |