sobol 是一种均匀数组替代随机半球采样,对降噪非常好
引用如下
vec2 ua=sobolVec2(int(frameCounter)+1,maxBounce);
vec3 L = SampleCosineHemisphere(ua.x, ua.y, N);
完整项目代码https://blog.csdn.net/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);
// 包围盒
node.AA = texelFetch(nodes, offset + 2).xyz;
node.BB = texelFetch(nodes, offset + 3).xyz;
return node;
}
// ----------------------------------------------------------------------------- //
// 光线和三角形求交
HitResult hitTriangle(Triangle triangle, Ray ray) {
HitResult res;
res.distance = INF;
res.isHit = false;
res.isInside = false;
vec3 p1 = triangle.p1;
vec3 p2 = triangle.p2;
vec3 p3 = triangle.p3;
vec3 S = ray.startPoint; // 射线起点
vec3 d = ray.direction; // 射线方向
vec3 N = normalize(cross(p2-p1, p3-p1)); // 法向量
// 从三角形背后(模型内部)击中
if (dot(N, d) > 0.0f) {
N = -N;
res.isInside = true;
}
// 如果视线和三角形平行
if (abs(dot(N, d)) < 0.00001f) return res;
// 距离
float t = (dot(N, p1) - dot(S, N)) / dot(d, N);
if (t < 0.0005f) return res; // 如果三角形在光线背面
// 交点计算
vec3 P = S + d * t;
// 判断交点是否在三角形中
vec3 c1 = cross(p2 - p1, P - p1);
vec3 c2 = cross(p3 - p2, P - p2);
vec3 c3 = cross(p1 - p3, P - p3);
bool r1 = (dot(c1, N) > 0 && dot(c2, N) > 0 && dot(c3, N) > 0);
bool r2 = (dot(c1, N) < 0 && dot(c2, N) < 0 && dot(c3, N) < 0);
// 命中,封装返回结果
if (r1 || r2) {
res.isHit = true;
res.hitPoint = P;
res.distance = t;
res.normal = N;
res.viewDir = d;
// 根据交点位置插值顶点法线
float alpha = (-(P.x-p2.x)*(p3.y-p2.y) + (P.y-p2.y)*(p3.x-p2.x)) / (-(p1.x-p2.x-0.00005)*(p3.y-p2.y+0.00005) + (p1.y-p2.y+0.00005)*(p3.x-p2.x+0.00005));
float beta = (-(P.x-p3.x)*(p1.y-p3.y) + (P.y-p3.y)*(p1.x-p3.x)) / (-(p2.x-p3.x-0.00005)*(p1.y-p3.y+0.00005) + (p2.y-p3.y+0.00005)*(p1.x-p3.x+0.00005));
float gama = 1.0 - alpha - beta;
vec3 Nsmooth = alpha * triangle.n1 + beta * triangle.n2 + gama * triangle.n3;
Nsmooth = normalize(Nsmooth);
res.normal = (res.isInside) ? (-Nsmooth) : (Nsmooth);
}
return res;
}
// 和 aabb 盒子求交,没有交点则返回 -1
float hitAABB(Ray r, vec3 AA, vec3 BB) {
vec3 invdir = 1.0 / r.direction;
vec3 f = (BB - r.startPoint) * invdir;
vec3 n = (AA - r.startPoint) * invdir;
vec3 tmax = max(f, n);
vec3 tmin = min(f, n);
float t1 = min(tmax.x, min(tmax.y, tmax.z));
float t0 = max(tmin.x, max(tmin.y, tmin.z));
return (t1 >= t0) ? ((t0 > 0.0) ? (t0) : (t1)) : (-1);
}
// ----------------------------------------------------------------------------- //
// 暴力遍历数组下标范围 [l, r] 求最近交点
HitResult hitArray(Ray ray, int l, int r) {
HitResult res;
res.isHit = false;
res.distance = INF;
for(int i=l; i<=r; i++) {
Triangle triangle = getTriangle(i);
HitResult r = hitTriangle(triangle, ray);
if(r.isHit && r.distance<res.distance) {
res = r;
res.material = getMaterial(i);
}
}
return res;
}
// 遍历 BVH 求交
HitResult hitBVH(Ray ray) {
HitResult res;
res.isHit = false;
res.distance = INF;
// 栈
int stack[256];
int sp = 0;
stack[sp++] = 1;
while(sp>0) {
int top = stack[--sp];
BVHNode node = getBVHNode(top);
// 是叶子节点,遍历三角形,求最近交点
if(node.n>0) {
int L = node.index;
int R = node.index + node.n - 1;
HitResult r = hitArray(ray, L, R);
if(r.isHit && r.distance<res.distance) res = r;
continue;
}
// 和左右盒子 AABB 求交
float d1 = INF; // 左盒子距离
float d2 = INF; // 右盒子距离
if(node.left>0) {
BVHNode leftNode = getBVHNode(node.left);
d1 = hitAABB(ray, leftNode.AA, leftNode.BB);
}
if(node.right>0) {
BVHNode rightNode = getBVHNode(node.right);
d2 = hitAABB(ray, rightNode.AA, rightNode.BB);
}
// 在最近的盒子中搜索
if(d1>0 && d2>0) {
if(d1<d2) { // d1<d2, 左边先
stack[sp++] = node.right;
stack[sp++] = node.left;
} else { // d2<d1, 右边先
stack[sp++] = node.left;
stack[sp++] = node.right;
}
} else if(d1>0) { // 仅命中左边
stack[sp++] = node.left;
} else if(d2>0) { // 仅命中右边
stack[sp++] = node.right;
}
}
return res;
}
// float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1);
// 1 ~ 8 维的 sobol 生成矩阵
const int V[8*32] =int [8*32](
2147483648, 1073741824, 536870912, 268435456, 134217728, 67108864, 33554432, 16777216, 8388608, 4194304, 2097152, 1048576, 524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1,
2147483648, 3221225472, 2684354560, 4026531840, 2281701376, 3422552064, 2852126720, 4278190080, 2155872256, 3233808384, 2694840320, 4042260480, 2290614272, 3435921408, 2863267840, 4294901760, 2147516416, 3221274624, 2684395520, 4026593280, 2281736192, 3422604288, 2852170240, 4278255360, 2155905152, 3233857728, 2694881440, 4042322160, 2290649224, 3435973836, 2863311530, 4294967295,
2147483648, 3221225472, 1610612736, 2415919104, 3892314112, 1543503872, 2382364672, 3305111552, 1753219072, 2629828608, 3999268864, 1435500544, 2154299392, 3231449088, 1626210304, 2421489664, 3900735488, 1556135936, 2388680704, 3314585600, 1751705600, 2627492864, 4008611328, 1431684352, 2147543168, 3221249216, 1610649184, 2415969680, 3892340840, 1543543964, 2382425838, 3305133397,
2147483648, 3221225472, 536870912, 1342177280, 4160749568, 1946157056, 2717908992, 2466250752, 3632267264, 624951296, 1507852288, 3872391168, 2013790208, 3020685312, 2181169152, 3271884800, 546275328, 1363623936, 4226424832, 1977167872, 2693105664, 2437829632, 3689389568, 635137280, 1484783744, 3846176960, 2044723232, 3067084880, 2148008184, 3222012020, 537002146, 1342505107,
2147483648, 1073741824, 536870912, 2952790016, 4160749568, 3690987520, 2046820352, 2634022912, 1518338048, 801112064, 2707423232, 4038066176, 3666345984, 1875116032, 2170683392, 1085997056, 579305472, 3016343552, 4217741312, 3719483392, 2013407232, 2617981952, 1510979072, 755882752, 2726789248, 4090085440, 3680870432, 1840435376, 2147625208, 1074478300, 537900666, 2953698205,
2147483648, 1073741824, 1610612736, 805306368, 2818572288, 335544320, 2113929216, 3472883712, 2290089984, 3829399552, 3059744768, 1127219200, 3089629184, 4199809024, 3567124480, 1891565568, 394297344, 3988799488, 920674304, 4193267712, 2950604800, 3977188352, 3250028032, 129093376, 2231568512, 2963678272, 4281226848, 432124720, 803643432, 1633613396, 2672665246, 3170194367,
2147483648, 3221225472, 2684354560, 3489660928, 1476395008, 2483027968, 1040187392, 3808428032, 3196059648, 599785472, 505413632, 4077912064, 1182269440, 1736704000, 2017853440, 2221342720, 3329785856, 2810494976, 3628507136, 1416089600, 2658719744, 864310272, 3863387648, 3076993792, 553150080, 272922560, 4167467040, 1148698640, 1719673080, 2009075780, 2149644390, 3222291575,
2147483648, 1073741824, 2684354560, 1342177280, 2281701376, 1946157056, 436207616, 2566914048, 2625634304, 3208642560, 2720006144, 2098200576, 111673344, 2354315264, 3464626176, 4027383808, 2886631424, 3770826752, 1691164672, 3357462528, 1993345024, 3752330240, 873073152, 2870150400, 1700563072, 87021376, 1097028000, 1222351248, 1560027592, 2977959924, 23268898, 437609937
);
// 格林码
int grayCode(int i) {
return i ^ (i>>1);
}
// 生成第 d 维度的第 i 个 sobol 数
float sobol(int d, int i) {
int result = 0;
int offset = d * 32;
for(int j = 0; i!=0; i >>= 1, j++) {
if((i & 1)!=0)
result ^= V[j+offset];
}
return float(result) * (1.0f/float(0xFFFFFFFFU));
}
// 生成第 i 帧的第 b 次反弹需要的二维随机向量
vec2 sobolVec2(int i, int b) {
float u = sobol(b*2, grayCode(i));
float v = sobol(b*2+1, grayCode(i));
return vec2(u, v);
}
float GTR1(float NdotH, float a) {
if (a >= 1) return 1/PI;
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return (a2-1) / (PI*log(a2)*t);
}
float GTR2(float NdotH, float a) {
float a2 = a*a;
float t = 1 + (a2-1)*NdotH*NdotH;
return a2 / (PI * t*t);
}
float SchlickFresnel(float u) {
float m = clamp(1-u, 0, 1);
float m2 = m*m;
return m2*m2*m; // pow(m,5)
}
float smithG_GGX(float NdotV, float alphaG) {
float a = alphaG*alphaG;
float b = NdotV*NdotV;
return 1 / (NdotV + sqrt(a + b - a*b));
}
vec3 BRDF_Evaluate(vec3 V, vec3 N, vec3 L, in Material material) {
float NdotL = dot(N, L);
float NdotV = dot(N, V);
if(NdotL < 0 || NdotV < 0) return vec3(0);
vec3 H = normalize(L + V);
float NdotH = dot(N, H);
float LdotH = dot(L, H);
vec3 Cdlin = material.baseColor;
float Cdlum = 1.0 * Cdlin.r + 1.0 * Cdlin.g + 1.0 * Cdlin.b;
vec3 Ctint = (Cdlum > 0) ? (Cdlin/Cdlum) : (vec3(1));
vec3 Cspec = material.specular * mix(vec3(1), Ctint, material.specularTint);
vec3 Cspec0 = mix(0.08*Cspec, Cdlin, material.metallic); // 0° 镜面反射颜色
// 镜面反射
float alpha = material.roughness * material.roughness;
float Ds = GTR2(NdotH, alpha);
float FH = SchlickFresnel(LdotH);
vec3 Fs = mix(Cspec0, vec3(1), FH);
float Gs = smithG_GGX(NdotL, material.roughness);
Gs *= smithG_GGX(NdotV, material.roughness);
vec3 specular = Gs * Fs * Ds;
// 漫反射
float Fd90 = 0.5 + 2.0 * LdotH * LdotH * material.roughness;
float FL = SchlickFresnel(NdotL);
float FV = SchlickFresnel(NdotV);
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
// 次表面散射
float Fss90 = LdotH * LdotH * material.roughness;
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
float ss = 1.25 * (Fss * (1.0 / (NdotL + NdotV) - 0.5) + 0.5);
// 清漆
float Dr = GTR1(NdotH, mix(0.1, 0.001, material.clearcoatGloss));
float Fr = mix(0.04, 1.0, FH);
float Gr = smithG_GGX(NdotL, 0.25) * smithG_GGX(NdotV, 0.25);
vec3 clearcoat = vec3(0.25 * Gr * Fr * Dr * material.clearcoat);
vec3 Csheen = mix(vec3(1), Ctint, material.sheenTint); // 织物颜色
// sheen 织物光泽
vec3 Fsheen = FH * material.sheen * Csheen;
//各向异性(anisotropic)
vec3 diffuse = (1.0/PI) * mix(Fd, ss, material.subsurface) * Cdlin;
diffuse += Fsheen;
return diffuse * (1.0 - material.metallic) + specular + clearcoat;
}
// 余弦加权的法向半球采样
vec3 SampleCosineHemisphere(float xi_1, float xi_2, vec3 N) {
// 均匀采样 xy 圆盘然后投影到 z 半球
float r = sqrt(xi_1);
float theta = xi_2 * 2.0 * PI;
float x = r * cos(theta);
float y = r * sin(theta);
float z = sqrt(1.0 - x*x - y*y);
// 从 z 半球投影到法向半球
vec3 L = toNormalHemisphere(vec3(x, y, z), N);
return L;
}
float rand(vec2 co){
return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453);
}
// GTR2 重要性采样
vec3 SampleGTR2(float xi_1, float xi_2, vec3 V, vec3 N, float alpha) {
float phi_h = 2.0 * PI * xi_1;
float sin_phi_h = sin(phi_h);
float cos_phi_h = cos(phi_h);
float cos_theta_h = sqrt((1.0-xi_2)/(1.0+(alpha*alpha-1.0)*xi_2));
float sin_theta_h = sqrt(max(0.0, 1.0 - cos_theta_h * cos_theta_h));
// 采样 "微平面" 的法向量 作为镜面反射的半角向量 h
vec3 H = vec3(sin_theta_h*cos_phi_h, sin_theta_h*sin_phi_h, cos_theta_h);
H = toNormalHemisphere(H, N); // 投影到真正的法向半球
// 根据 "微法线" 计算反射光方向
vec3 L = reflect(-V, H);
return L;
}
// GTR1 重要性采样
vec3 SampleGTR1(float xi_1, float xi_2, vec3 V, vec3 N, float alpha) {
float phi_h = 2.0 * PI * xi_1;
float sin_phi_h = sin(phi_h);
float cos_phi_h = cos(phi_h);
float cos_theta_h = sqrt((1.0-pow(alpha*alpha, 1.0-xi_2))/(1.0-alpha*alpha));
float sin_theta_h = sqrt(max(0.0, 1.0 - cos_theta_h * cos_theta_h));
// 采样 "微平面" 的法向量 作为镜面反射的半角向量 h
vec3 H = vec3(sin_theta_h*cos_phi_h, sin_theta_h*sin_phi_h, cos_theta_h);
H = toNormalHemisphere(H, N); // 投影到真正的法向半球
// 根据 "微法线" 计算反射光方向
vec3 L = reflect(-V, H);
return L;
}
float sqr(float x) {
return x*x;
}
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay) {
return 1 / (PI * ax*ay * sqr( sqr(HdotX/ax) + sqr(HdotY/ay) + NdotH*NdotH ));
}
// 按照辐射度分布分别采样三种 BRDF
vec3 SampleBRDF(float xi_1, float xi_2, float xi_3, vec3 V, vec3 N, in Material material) {
float alpha_GTR1 = mix(0.1, 0.001, material.clearcoatGloss);
float alpha_GTR2 = max(0.001, sqr(material.roughness));
// 辐射度统计
float r_diffuse = (1.0 - material.metallic);
float r_specular = 1.0;
float r_clearcoat = 0.25 * material.clearcoat;
float r_sum = r_diffuse + r_specular + r_clearcoat;
// 根据辐射度计算概率
float p_diffuse = r_diffuse / r_sum;
float p_specular = r_specular / r_sum;
float p_clearcoat = r_clearcoat / r_sum;
// 按照概率采样
float rd = xi_3;
// 漫反射
if(rd <= p_diffuse) {
return SampleCosineHemisphere(xi_1, xi_2, N);
}
// 镜面反射
else if(p_diffuse < rd && rd <= p_diffuse + p_specular) {
return SampleGTR2(xi_1, xi_2, V, N, alpha_GTR2);
}
// 清漆
else if(p_diffuse + p_specular < rd) {
return SampleGTR1(xi_1, xi_2, V, N, alpha_GTR1);
}
return vec3(0, 1, 0);
}
// 获取 BRDF 在 L 方向上的概率密度
float BRDF_Pdf(vec3 V, vec3 N, vec3 L, in Material material) {
float NdotL = dot(N, L);
float NdotV = dot(N, V);
if(NdotL < 0 || NdotV < 0) return 0;
vec3 H = normalize(L + V);
float NdotH = dot(N, H);
float LdotH = dot(L, H);
// 镜面反射 -- 各向同性
float alpha = max(0.001, sqr(material.roughness));
float Ds = GTR2(NdotH, alpha);
float Dr = GTR1(NdotH, mix(0.1, 0.001, material.clearcoatGloss)); // 清漆
// 分别计算三种 BRDF 的概率密度
float pdf_diffuse = NdotL / PI;
float pdf_specular = Ds * NdotH / (4.0 * dot(L, H));
float pdf_clearcoat = Dr * NdotH / (4.0 * dot(L, H));
// 辐射度统计
float r_diffuse = (1.0 - material.metallic);
float r_specular = 1.0;
float r_clearcoat = 0.25 * material.clearcoat;
float r_sum = r_diffuse + r_specular + r_clearcoat;
// 根据辐射度计算选择某种采样方式的概率
float p_diffuse = r_diffuse / r_sum;
float p_specular = r_specular / r_sum;
float p_clearcoat = r_clearcoat / r_sum;
// 根据概率混合 pdf
float pdf = p_diffuse * pdf_diffuse
+ p_specular * pdf_specular
+ p_clearcoat * pdf_clearcoat;
pdf = max(1e-10, pdf);
return pdf;
}
// ----------------------------------------------------------------------------- //
// 路径追踪
// 路径追踪 -- 重要性采样版本
vec3 pathTracingImportanceSampling(HitResult hit, int maxBounce) {
vec3 Lo = vec3(0); // 最终的颜色
vec3 history = vec3(1); // 递归积累的颜色
for(int bounce=0; bounce<maxBounce; bounce++) {
vec3 V = -hit.viewDir;
vec3 N = hit.normal;
// 获取 3 个随机数
float xi_1;
float xi_2;
float xi_3;
xi_1 =rand(vec2(777.9898,5125.233));
xi_2 =rand(vec2(777.9898,5125.233));
xi_3 =rand(vec2(777.9898,5125.233));
//vec3 L = SampleCosineHemisphere(xi_1, xi_2, N);
// 采样 BRDF 得到一个方向 L
vec3 L = SampleBRDF(xi_1, xi_2, xi_3, V, N, hit.material);
float NdotL = dot(N, L);
if(NdotL <= 0.0) break;
// 发射光线
Ray randomRay;
randomRay.startPoint = hit.hitPoint;
randomRay.direction = L;
HitResult newHit = hitBVH(randomRay);
// 获取 L 方向上的 BRDF 值和概率密度
vec3 f_r = BRDF_Evaluate(V, N, L, hit.material);
float pdf_brdf = BRDF_Pdf(V, N, L, hit.material);
if(pdf_brdf <= 0.0) break;
// 未命中
if(!newHit.isHit) {
//vec3 color = vec3(0.5,0.0,0.5);// vec3 color = hdrColor(L);
Lo += history * f_r * NdotL / pdf_brdf;
break;
}
// 命中光源积累颜色
vec3 Le = newHit.material.emissive;
Lo += history * Le * f_r * NdotL / pdf_brdf;
// 递归(步进)
hit = newHit;
history *= f_r * NdotL / pdf_brdf; // 累积颜色
}
return Lo;
}
// 路径追踪
vec3 pathTracing(HitResult hit, int maxBounce) {
vec3 Lo = vec3(0); // 最终的颜色
vec3 history = vec3(1); // 递归积累的颜色
for(int bounce=0; bounce<maxBounce; bounce++) {
// 随机出射方向 wi
vec3 wi = toNormalHemisphere(SampleHemisphere(), hit.normal);
// 漫反射: 随机发射光线
Ray randomRay;
randomRay.startPoint = hit.hitPoint;
randomRay.direction = wi;
HitResult newHit = hitBVH(randomRay);
float pdf = 1.0 / (2.0 * PI); // 半球均匀采样概率密度
float cosine_o = max(0, dot(-hit.viewDir, hit.normal)); // 入射光和法线夹角余弦
float cosine_i = max(0, dot(randomRay.direction, hit.normal)); // 出射光和法线夹角余弦
//vec3 f_r = hit.material.baseColor / PI; // 漫反射 BRDF
vec3 V = -hit.viewDir;
vec3 N = hit.normal;
//vec3 L = toNormalHemisphere(SampleHemisphere(), hit.normal); // 随机出射方向 wi
vec2 ua=sobolVec2(int(frameCounter)+1,maxBounce);
vec3 L = SampleCosineHemisphere(ua.x, ua.y, N);
vec3 f_r = BRDF_Evaluate(V, N, L, hit.material);
// 未命中
if(!newHit.isHit) {
vec3 skyColor = sampleHdr(randomRay.direction);
Lo += history * skyColor * f_r * cosine_i / pdf;
break;
}
// 命中光源积累颜色
vec3 Le = newHit.material.emissive;
Lo += history * Le * f_r * cosine_i / pdf;
// 递归(步进)
hit = newHit;
history *= f_r * cosine_i / pdf; // 累积颜色
}
return Lo;
}
// ----------------------------------------------------------------------------- //
void main() {
// 投射光线
Ray ray;
ray.startPoint = eye;
vec2 AA = vec2((rand()-0.5)/float(width), (rand()-0.5)/float(height));
vec4 dir = cameraRotate * vec4(pix.xy+AA, -1.5, 0.0);
ray.direction = normalize(dir.xyz);
// primary hit
HitResult firstHit = hitBVH(ray);
vec3 color;
if(!firstHit.isHit) {
color = vec3(0);
color = sampleHdr(ray.direction);
} else {
vec3 Le = firstHit.material.emissive;
vec3 Li = pathTracing(firstHit, 4);
// vec3 Li = pathTracingImportanceSampling(firstHit, 4);//路径追踪重要性采样
color = Le + Li;
}
// 和上一帧混合
vec3 lastColor = texture2D(lastFrame, pix.xy*0.5+0.5).rgb;
color = mix(lastColor, color, 1.0/(float(frameCounter)+1));
// 输出
gl_FragData[0] = vec4(color, 1.0);
}