Global Illumination_Voxel Area Lighting (VXAL)

在这里插入图片描述

一、VXAL

Voxel Area Lighting (VXAL)主要是基于体素的面光源处理,其中主要有两种处理方法:

1.1 自发光材质处理面光源

本方法处理最为简单,基本不用更改原有VXGI的所有流程,仅需在体素化过程中存储自发光材质的相关特性,之后在处理Cone Tracing的时候正常采样体素即可,其中可以自定义材质的Diffuse与Specular来实现不同的材质特性。

1.2 面光源上均匀分布Cone

本方法是NVIDIA VXGI2.0中对VXAL技术的提升,其对外API提供了如下接口可以来更好的设置面光源:
在这里插入图片描述
通过上述接口调用,可以直接获取VXAL的相关辐照信息供渲染使用(其中不仅包含LTC光源处理算法,还包括了更多的全局光照技术),其具体原理如下:

  1. 在面光源均匀分布Cone,对采样点贡献,其中影响因素包括着色点距离衰减、光源尺寸影响Cone个数等多重影响

在这里插入图片描述

  1. 之后处理相关遮蔽数据:其中对于大比例场景,即clipMap较大体素使用VXAO技术,对细节场景采用屏幕空间阴影技术。

在这里插入图片描述

  1. 接下来处理面光源对场景的辐照影响,其中包含Diffuse与Specular两项,并且使用了LTC技术对不规则面光源进行更好的处理(LTC算法后续再具体研究)

在这里插入图片描述

  1. 之后调整下遮蔽阴影与光源的融合,可见如下:

在这里插入图片描述

  1. 最后将光源材质等信息叠加至场景,可渲染整体如下

在这里插入图片描述
最后放一张其官宣演示渲染场景:
在这里插入图片描述

本部分主要使用自发光材质实现面光源。

二、代码实现

简单的实现方式可以将面光源当成自发光材质进行处理,在cone tracing的时候正常采样,可根据物体表面材质特性自行控制Diffuse与Specular的占比来实现不同材质表现。

2.1 体素化的处理过程

以下代码主要为体素化PS过程中的处理,重点查看注释部分说明即可。

其中需要注意的是,相对于之前VXGI文章的区别是增加了emissiveMap贴图,并将采样纹理贴图的信息也一并存储到体素中

#version 450 core

// 灯光设置.
#define POINT_LIGHT_INTENSITY 1
#define MAX_LIGHTS 1

// 灯光设置衰减因子.
#define DIST_FACTOR 1.1f /* Distance is multiplied by this when calculating attenuation. */
#define CONSTANT 1
#define LINEAR 0
#define QUADRATIC 1

// 返回给定距离的衰减因子  .
float attenuate(float dist){ dist *= DIST_FACTOR; return 1.0f / (CONSTANT + LINEAR * dist + QUADRATIC * dist * dist); }

struct PointLight {
	vec3 position;
	vec3 color;
};

struct Material {
	vec3 diffuseColor;
	vec3 specularColor;
	float diffuseReflectivity;
	float specularReflectivity;
	float emissivity;//自发光特性
	float transparency;
};
layout(binding = 0) uniform sampler2D emissiveMap;
uniform Material material;
uniform PointLight pointLights[MAX_LIGHTS];
uniform int numberOfLights;
uniform vec3 cameraPosition;
layout(RGBA8) uniform image3D texture3D;

in vec3 worldPositionFrag;
in vec3 normalFrag;

vec3 calculatePointLight(const PointLight light){
	const vec3 direction = normalize(light.position - worldPositionFrag);
	const float distanceToLight = distance(light.position, worldPositionFrag);
	const float attenuation = attenuate(distanceToLight);
	const float d = max(dot(normalize(normalFrag), direction), 0.0f);
	return d * POINT_LIGHT_INTENSITY * attenuation * light.color;
};

vec3 scaleAndBias(vec3 p) { return 0.5f * p + vec3(0.5f); }

bool isInsideCube(const vec3 p, float e) { return abs(p.x) < 1 + e && abs(p.y) < 1 + e && abs(p.z) < 1 + e; }

void main(){
	vec3 color = vec3(0.0f);
	if(!isInsideCube(worldPositionFrag, 0)) return;
	
	const uint maxLights = min(numberOfLights, MAX_LIGHTS);
	for(uint i = 0; i < maxLights; ++i) color += calculatePointLight(pointLights[i]);
	vec3 spec = material.specularReflectivity * material.specularColor;
	vec3 diff = material.diffuseReflectivity * material.diffuseColor;
	
	//体素化过程中存储自发光面光源的特性
	vec4 emissive = texture(emissiveMap, texCoord.xy);
	color = (diff + spec) * color + clamp(material.emissivity, 0, 1) * material.diffuseColor * emissive.rgb ;

	vec3 voxel = scaleAndBias(worldPositionFrag);
	ivec3 dim = imageSize(texture3D);
	float alpha = pow(1 - material.transparency, 4); // For soft shadows to work better with transparent materials.
	vec4 res = alpha * vec4(vec3(color), 1);
    imageStore(texture3D, ivec3(dim * voxel), res);
}

经过上述处理后对面光源的体素化表示如下:
在这里插入图片描述

2.2 Cone Tracing过程的处理

其中主要查看自发光部分的处理即可。

#version 450 core
#define TSQRT2 2.828427
#define SQRT2 1.414213
#define ISQRT2 0.707106
// --------------------------------------
// 光(体素)圆锥跟踪设置.
// --------------------------------------
#define MIPMAP_HARDCAP 5.4f /* mipmap层级,影响渲染效果. */
#define VOXEL_SIZE (1/64.0) /* 体素的大小. 128x128x128 => 1/128 = 0.0078125. */
#define SHADOWS 1 /* 阴影开关. */
#define DIFFUSE_INDIRECT_FACTOR 0.52f /* 漫射间接光照的强度. */
// --------------------------------------
// 其余光照设置.
// --------------------------------------
#define SPECULAR_MODE 1 /* 0 == Blinn-Phong , 1 == reflection model. */
#define SPECULAR_FACTOR 4.0f /* 镜面强度调整因子. */
#define SPECULAR_POWER 65.0f /* 镜面强度粗糙度. */
#define DIRECT_LIGHT_INTENSITY 0.96f /* (直接)点光强度因子. */
#define MAX_LIGHTS 1 /* 灯光数. */

// 光照衰减的因子。 可参见函数“attenuate()” .
#define DIST_FACTOR 1.1f /* 在计算衰减时,距离乘以这个系数. */
#define CONSTANT 1
#define LINEAR 0 /* . */
#define QUADRATIC 1

// 其他数据.
#define GAMMA_CORRECTION 1 /* 是否使用伽马校正. */

// 基础点光源.
struct PointLight {
	vec3 position;
	vec3 color;
};

// 基础材质.
struct Material {
	vec3 diffuseColor;
	float diffuseReflectivity;
	vec3 specularColor;
	float specularDiffusion; // “反射和折射”镜面扩散. 
	float specularReflectivity;
	float emissivity; // 发射材料使用漫射色作为发射色 .
	float refractiveIndex;
	float transparency;
};

layout(binding = 0) uniform sampler2D gEmissive;

struct Settings {
	bool indirectSpecularLight; // 是否渲染间接反射光.
	bool indirectDiffuseLight; // 是否渲染间接漫射光.
	bool directLight; // 是否要渲染直射光.
	bool shadows; // 是否渲染阴影.
};

uniform Material material;
uniform Settings settings;
uniform PointLight pointLights[MAX_LIGHTS];
uniform int numberOfLights; // 灯光数.
uniform vec3 cameraPosition; // 世界空间下相机位置.
uniform int state; // .
uniform sampler3D texture3D; // 体素纹理.

in vec3 worldPositionFrag;
in vec3 normalFrag;

out vec4 color;

vec3 normal = normalize(normalFrag); 
float MAX_DISTANCE = distance(vec3(abs(worldPositionFrag)), vec3(-1));

// 返回给定距离的衰减因子 .
float attenuate(float dist){ dist *= DIST_FACTOR; return 1.0f / (CONSTANT + LINEAR * dist + QUADRATIC * dist * dist); }

// 返回一个正交于u的向量.
vec3 orthogonal(vec3 u){
	u = normalize(u);
	vec3 v = vec3(0.99146, 0.11664, 0.05832); // Pick any normalized vector.
	return abs(dot(u, v)) > 0.99999f ? cross(u, vec3(0, 1, 0)) : cross(u, v);
}

// (from [-1, 1] to [0, 1]).
vec3 scaleAndBias(const vec3 p) { return 0.5f * p + vec3(0.5f); }

// 如果点p在单位立方体内,返回true  . 
bool isInsideCube(const vec3 p, float e) { return abs(p.x) < 1 + e && abs(p.y) < 1 + e && abs(p.z) < 1 + e; }

//使用阴影圆锥跟踪返回一个柔和的阴影混合。  
//每个步骤使用2个样本,性能开销较大。  
float traceShadowCone(vec3 from, vec3 direction, float targetDistance){
	from += normal * 0.05f; 

	float acc = 0;

	float dist = 3 * VOXEL_SIZE;
	// 影响范围大小.
	const float STOP = targetDistance - 16 * VOXEL_SIZE;

	while(dist < STOP && acc < 1){	
		vec3 c = from + dist * direction;
		if(!isInsideCube(c, 0)) break;
		c = scaleAndBias(c);
		float l = pow(dist, 2); // 对阴影进行平方衰减.
		float s1 = 0.062 * textureLod(texture3D, c, 1 + 0.75 * l).a;
		float s2 = 0.135 * textureLod(texture3D, c, 4.5 * l).a;
		float s = s1 + s2;
		acc += (1 - acc) * s;
		dist += 0.9 * VOXEL_SIZE * (1 + 0.05 * l);
	}
	return 1 - pow(smoothstep(0, 1, acc * 1.4), 1.0 / 1.4);
}	

// 追踪漫反射体素锥.
vec3 traceDiffuseVoxelCone(const vec3 from, vec3 direction){
	direction = normalize(direction);
	
	const float CONE_SPREAD = 0.325;

	vec4 acc = vec4(0.0f);

	//控制闭合表面漏光情况。  
	//如果使用阴影圆锥跟踪,较低的值会导致效果很差。    
	float dist = 0.1953125;

	// Trace.
	while(dist < SQRT2 && acc.a < 1){
		vec3 c = from + dist * direction;
		c = scaleAndBias(from + dist * direction);
		float l = (1 + CONE_SPREAD * dist / VOXEL_SIZE);
		float level = log2(l);
		float ll = (level + 1) * (level + 1);
		vec4 voxel = textureLod(texture3D, c, min(MIPMAP_HARDCAP, level));
		acc += 0.075 * ll * voxel * pow(1 - voxel.a, 2);
		dist += ll * VOXEL_SIZE * 2;
	}
	return pow(acc.rgb * 2.0, vec3(1.5));
}

// 追踪高光体素锥.
vec3 traceSpecularVoxelCone(vec3 from, vec3 direction){
	direction = normalize(direction);

	const float OFFSET = 8 * VOXEL_SIZE;
	const float STEP = VOXEL_SIZE;

	from += OFFSET * normal;
	
	vec4 acc = vec4(0.0f);
	float dist = OFFSET;

	// Trace.
	while(dist < MAX_DISTANCE && acc.a < 1){ 
		vec3 c = from + dist * direction;
		if(!isInsideCube(c, 0)) break;
		c = scaleAndBias(c); 
		
		float level = 0.1 * material.specularDiffusion * log2(1 + dist / VOXEL_SIZE);
		vec4 voxel = textureLod(texture3D, c, min(level, MIPMAP_HARDCAP));
		float f = 1 - acc.a;
		acc.rgb += 0.25 * (1 + material.specularDiffusion) * voxel.rgb * voxel.a * f;
		acc.a += 0.25 * voxel.a * f;
		dist += STEP * (1.0f + 0.125f * level);
	}
	return 1.0 * pow(material.specularDiffusion + 1, 0.8) * acc.rgb;
}

//使用体素锥追踪计算间接漫射光。当前的实现与展示的一样使用9个圆锥。   

vec3 indirectDiffuseLight(){
	const float ANGLE_MIX = 0.5f; // 角度混合(1.0f =>正交方向,0.0f =>法线方向)  .

	const float w[3] = {1.0, 1.0, 1.0}; // 锥的权重.

	// 求边锥的底,法向量为其底向量之一.
	const vec3 ortho = normalize(orthogonal(normal));
	const vec3 ortho2 = normalize(cross(ortho, normal));

	// 求角锥的基向量.
	const vec3 corner = 0.5f * (ortho + ortho2);
	const vec3 corner2 = 0.5f * (ortho - ortho2);

	//找到跟踪的起始位置(从偏移量开始)   .
	const vec3 N_OFFSET = normal * (1 + 4 * ISQRT2) * VOXEL_SIZE;
	const vec3 C_ORIGIN = worldPositionFrag + N_OFFSET;

	// 累积间接漫射光用.
	vec3 acc = vec3(0);

	//我们在法线方向上向前偏移,在圆锥方向上向后偏移。  
	//向后锥体方向可以改善GI,而向前方向可以移除artifacts。 
	const float CONE_OFFSET = -0.01;

	// 追踪前锥
	acc += w[0] * traceDiffuseVoxelCone(C_ORIGIN + CONE_OFFSET * normal, normal);

	// 追踪4个边锥.
	const vec3 s1 = mix(normal, ortho, ANGLE_MIX);
	const vec3 s2 = mix(normal, -ortho, ANGLE_MIX);
	const vec3 s3 = mix(normal, ortho2, ANGLE_MIX);
	const vec3 s4 = mix(normal, -ortho2, ANGLE_MIX);

	acc += w[1] * traceDiffuseVoxelCone(C_ORIGIN + CONE_OFFSET * ortho, s1);
	acc += w[1] * traceDiffuseVoxelCone(C_ORIGIN - CONE_OFFSET * ortho, s2);
	acc += w[1] * traceDiffuseVoxelCone(C_ORIGIN + CONE_OFFSET * ortho2, s3);
	acc += w[1] * traceDiffuseVoxelCone(C_ORIGIN - CONE_OFFSET * ortho2, s4);

	// 追踪4个中心锥.
	const vec3 c1 = mix(normal, corner, ANGLE_MIX);
	const vec3 c2 = mix(normal, -corner, ANGLE_MIX);
	const vec3 c3 = mix(normal, corner2, ANGLE_MIX);
	const vec3 c4 = mix(normal, -corner2, ANGLE_MIX);

	acc += w[2] * traceDiffuseVoxelCone(C_ORIGIN + CONE_OFFSET * corner, c1);
	acc += w[2] * traceDiffuseVoxelCone(C_ORIGIN - CONE_OFFSET * corner, c2);
	acc += w[2] * traceDiffuseVoxelCone(C_ORIGIN + CONE_OFFSET * corner2, c3);
	acc += w[2] * traceDiffuseVoxelCone(C_ORIGIN - CONE_OFFSET * corner2, c4);

	// 返回结果
	return DIFFUSE_INDIRECT_FACTOR * material.diffuseReflectivity * acc * (material.diffuseColor + vec3(0.001f));
}

// 使用体素圆锥追踪计算间接镜面光。  
vec3 indirectSpecularLight(vec3 viewDirection){
	const vec3 reflection = normalize(reflect(viewDirection, normal));
	return material.specularReflectivity * material.specularColor * traceSpecularVoxelCone(worldPositionFrag, reflection);
}

// 使用体素锥追踪计算折射光 .
vec3 indirectRefractiveLight(vec3 viewDirection){
	const vec3 refraction = refract(viewDirection, normal, 1.0 / material.refractiveIndex);
	const vec3 cmix = mix(material.specularColor, 0.5 * (material.specularColor + vec3(1)), material.transparency);
	return cmix * traceSpecularVoxelCone(worldPositionFrag, refraction);
}

//计算一个给定点光的漫反射和镜面直射光。  
//使用阴影圆锥跟踪软阴影。 
vec3 calculateDirectLight(const PointLight light, const vec3 viewDirection){
	vec3 lightDirection = light.position - worldPositionFrag;
	const float distanceToLight = length(lightDirection);
	lightDirection = lightDirection / distanceToLight;
	const float lightAngle = dot(normal, lightDirection);
	
	// --------------------
	// Diffuse lighting.
	// --------------------
	float diffuseAngle = max(lightAngle, 0.0f); // Lambertian.	
	
	// --------------------
	// Specular lighting.
	// --------------------
#if (SPECULAR_MODE == 0) /* Blinn-Phong. */
	const vec3 halfwayVector = normalize(lightDirection + viewDirection);
	float specularAngle = max(dot(normal, halfwayVector), 0.0f);
#endif
	
#if (SPECULAR_MODE == 1) /* Perfect reflection. */
	const vec3 reflection = normalize(reflect(viewDirection, normal));
	float specularAngle = max(0, dot(reflection, lightDirection));
#endif

	float refractiveAngle = 0;
	if(material.transparency > 0.01){
		vec3 refraction = refract(viewDirection, normal, 1.0 / material.refractiveIndex);
		refractiveAngle = max(0, material.transparency * dot(refraction, lightDirection));
	}

	// --------------------
	// Shadows.
	// --------------------
	float shadowBlend = 1;
#if (SHADOWS == 1)
	if(diffuseAngle * (1.0f - material.transparency) > 0 && settings.shadows)
		shadowBlend = traceShadowCone(worldPositionFrag, lightDirection, distanceToLight);
#endif

	// --------------------
	// 光照叠加.
	// --------------------
	diffuseAngle = min(shadowBlend, diffuseAngle);
	specularAngle = min(shadowBlend, max(specularAngle, refractiveAngle));
	const float df = 1.0f / (1.0f + 0.25f * material.specularDiffusion); // Diffusion factor.
	const float specular = SPECULAR_FACTOR * pow(specularAngle, df * SPECULAR_POWER);
	const float diffuse = diffuseAngle * (1.0f - material.transparency);

	const vec3 diff = material.diffuseReflectivity * material.diffuseColor * diffuse;
	const vec3 spec = material.specularReflectivity * material.specularColor * specular;
	const vec3 total = light.color * (diff + spec);
	return attenuate(distanceToLight) * total;
};

// 累加所有来自点光源的直接光线(包括漫射和镜面) .
vec3 directLight(vec3 viewDirection){
	vec3 direct = vec3(0.0f);
	const uint maxLights = min(numberOfLights, MAX_LIGHTS);
	for(uint i = 0; i < maxLights; ++i) direct += calculateDirectLight(pointLights[i], viewDirection);
	direct *= DIRECT_LIGHT_INTENSITY;
	return direct;
}

void main(){
	color = vec4(0, 0, 0, 1);
	const vec3 viewDirection = normalize(worldPositionFrag - cameraPosition);

	// 间接的漫射光.
	if(settings.indirectDiffuseLight && material.diffuseReflectivity * (1.0f - material.transparency) > 0.01f) 
		color.rgb += indirectDiffuseLight();

	// 间接镜面光(镜面反射).
	if(settings.indirectSpecularLight && material.specularReflectivity * (1.0f - material.transparency) > 0.01f) 
		color.rgb += indirectSpecularLight(viewDirection);

    // 采样自发光贴图
    vec3 emissive = texture(gEmissive, texCoord).rgb;
    
	// 自发光.
	color.rgb += material.emissivity * emissive ;

	// 透明
	if(material.transparency > 0.01f)
		color.rgb = mix(color.rgb, indirectRefractiveLight(viewDirection), material.transparency);

	// 直接光照.
	if(settings.directLight)
		color.rgb += directLight(viewDirection);

#if (GAMMA_CORRECTION == 1)
	color.rgb = pow(color.rgb, vec3(1.0 / 2.2));
#endif
}

处理完之后,可如下渲染结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值