sobol 是一种均匀数组替代随机半球采样,对降噪非常好
引用如下
vec2 ua=sobolVec2(int(frameCounter)+1,maxBounce);
vec3 L = SampleCosineHemisphere(ua.x, ua.y, N);
完整项目代码https://blog.youkuaiyun.com/asaqlp/article/details/120828724
fshader.fsh
#version 330 core
in vec3 pix;
out vec4 fragColor;
// ----------------------------------------------------------------------------- //
uniform uint frameCounter;
uniform int nTriangles;
uniform int nNodes;
uniform int width;
uniform int height;
uniform samplerBuffer triangles;
uniform samplerBuffer nodes;
uniform sampler2D lastFrame;
uniform sampler2D hdrMap;
uniform vec3 eye;
uniform mat4 cameraRotate;
// ----------------------------------------------------------------------------- //
#define PI 3.1415926
#define INF 114514.0
#define SIZE_TRIANGLE 12
#define SIZE_BVHNODE 4
// ----------------------------------------------------------------------------- //
// Triangle 数据格式
struct Triangle {
vec3 p1, p2, p3; // 顶点坐标
vec3 n1, n2, n3; // 顶点法线
};
// BVH 树节点
struct BVHNode {
int left; // 左子树
int right; // 右子树
int n; // 包含三角形数目
int index; // 三角形索引
vec3 AA, BB; // 碰撞盒
};
// 物体表面材质定义
struct Material {
vec3 emissive; // 作为光源时的发光颜色
vec3 baseColor;
float subsurface;
float metallic;
float specular;
float specularTint;
float roughness;
float anisotropic;
float sheen;
float sheenTint;
float clearcoat;
float clearcoatGloss;
float IOR;
float transmission;
};
// 光线
struct Ray {
vec3 startPoint;
vec3 direction;
};
// 光线求交结果
struct HitResult {
bool isHit; // 是否命中
bool isInside; // 是否从内部命中
float distance; // 与交点的距离
vec3 hitPoint; // 光线命中点
vec3 normal; // 命中点法线
vec3 viewDir; // 击中该点的光线的方向
Material material; // 命中点的表面材质
};
// ----------------------------------------------------------------------------- //
/*
* 生成随机向量,依赖于 frameCounter 帧计数器
* 代码来源:https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/
*/
uint seed = uint(
uint((pix.x * 0.5 + 0.5) * width) * uint(1973) +
uint((pix.y * 0.5 + 0.5) * height) * uint(9277) +
uint(frameCounter) * uint(26699)) | uint(1);
uint wang_hash(inout uint seed) {
seed = uint(seed ^ uint(61)) ^ uint(seed >> uint(16));
seed *= uint(9);
seed = seed ^ (seed >> 4);
seed *= uint(0x27d4eb2d);
seed = seed ^ (seed >> 15);
return seed;
}
float rand() {
return float(wang_hash(seed)) / 4294967296.0;
}
// ----------------------------------------------------------------------------- //
// 半球均匀采样
vec3 SampleHemisphere() {
float z = rand();
float r = max(0, sqrt(1.0 - z*z));
float phi = 2.0 * PI * rand();
return vec3(r * cos(phi), r * sin(phi), z);
}
/*
vec3 toNormalHemisphere(vec3 v, vec3 N) {
vec3 tangent = vec3(0);
if(N.yz==vec2(0)) tangent = vec3(0, 0, -N.x);
else if(N.xz==vec2(0)) tangent = vec3(0, 0, N.y);
else if(N.xy==vec2(0)) tangent = vec3(-N.z, 0, 0);
else if(abs(N.x)>abs(N.y)) tangent = normalize(vec3(0, N.z, -N.y));
else tangent = normalize(vec3(-N.z, 0, N.x));
vec3 bitangent = cross(N, tangent);
return normalize(v.x * tangent + v.y * bitangent + v.z * N);
}
*/
// 将向量 v 投影到 N 的法向半球
vec3 toNormalHemisphere(vec3 v, vec3 N) {
vec3 helper = vec3(1, 0, 0);
if(abs(N.x)>0.999) helper = vec3(0, 0, 1);
vec3 tangent = normalize(cross(N, helper));
vec3 bitangent = normalize(cross(N, tangent));
return v.x * tangent + v.y * bitangent + v.z * N;
}
// ----------------------------------------------------------------------------- //
// 将三维向量 v 转为 HDR map 的纹理坐标 uv
vec2 SampleSphericalMap(vec3 v) {
vec2 uv = vec2(atan(v.z, v.x), asin(v.y));
uv /= vec2(2.0 * PI, PI);
uv += 0.5;
uv.y = 1.0 - uv.y;
return uv;
}
// 获取 HDR 环境颜色
vec3 sampleHdr(vec3 v) {
vec2 uv = SampleSphericalMap(normalize(v));
vec3 color = texture2D(hdrMap, uv).rgb;
color = min(color, vec3(10));
return color;
}
// ----------------------------------------------------------------------------- //
// 获取第 i 下标的三角形
Triangle getTriangle(int i) {
int offset = i * SIZE_TRIANGLE;
Triangle t;
// 顶点坐标
t.p1 = texelFetch(triangles, offset + 0).xyz;
t.p2 = texelFetch(triangles, offset + 1).xyz;
t.p3 = texelFetch(triangles, offset + 2).xyz;
// 法线
t.n1 = texelFetch(triangles, offset + 3).xyz;
t.n2 = texelFetch(triangles, offset + 4).xyz;
t.n3 = texelFetch(triangles, offset + 5).xyz;
return t;
}
// 获取第 i 下标的三角形的材质
Material getMaterial(int i) {
Material m;
int offset = i * SIZE_TRIANGLE;
vec3 param1 = texelFetch(triangles, offset + 8).xyz;
vec3 param2 = texelFetch(triangles, offset + 9).xyz;
vec3 param3 = texelFetch(triangles, offset + 10).xyz;
vec3 param4 = texelFetch(triangles, offset + 11).xyz;
m.emissive = texelFetch(triangles, offset + 6).xyz;
m.baseColor = texelFetch(triangles, offset + 7).xyz;
m.subsurface = param1.x;
m.metallic = param1.y;
m.specular = param1.z;
m.specularTint = param2.x;
m.roughness = param2.y;
m.anisotropic = param2.z;
m.sheen = param3.x;
m.sheenTint = param3.y;
m.clearcoat = param3.z;
m.clearcoatGloss = param4.x;
m.IOR = param4.y;
m.transmission = param4.z;
return m;
}
// 获取第 i 下标的 BVHNode 对象
BVHNode getBVHNode(int i) {
BVHNode node;
// 左右子树
int offset = i * SIZE_BVHNODE;
ivec3 childs = ivec3(texelFetch(nodes, offset + 0).xyz);
ivec3 leafInfo = ivec3(texelFetch(nodes, offset + 1).xyz);
node.left = int(childs.x);
node.right = int(childs.y);
node.n = int(leafInfo.x);
node.index = int(leafInfo.y);
//