vec3 color = vec3(0.5,0.0,0.5);// vec3 color = hdrColor(L);
hdrColor() 函数 不知道是什么,随便返回 一个颜色
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 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 * color * 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;
}
// ----------------------------------------------------------------------------- //
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 = pathTracingImportanceSampling(firstHit, 2);
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);
}