当前位置:网站首页>GAMES101作业7提高-微表面材质的实现过程
GAMES101作业7提高-微表面材质的实现过程
2022-06-27 03:12:00 【flashinggg】
目录
提高部分作业要求:
看之前可以结合我的另一篇博客一起看:GAMES101作业7提高-实现微表面模型你需要了解的知识
我们要做什么?主要操作是在Material.hpp里完成的。
补充Material.hpp
补充计算着色函数-eval()
提高部分要实现的微表面模型,其实跟DIFFUSE一样,也是一种材质,需要通过一系列计算得到BRDF的值,即路径Scene.cpp -> castRay()函数代码中,直接光照和间接光照计算公式中f_r的值:
dir = L_i * f_r * cos_theta * cos_theta_l / dis2 / pdf_light;
indir = castRay(r, depth + 1) * f_r * cos_theta / pdf_hemi / RussianRoulette;
赋值分别是:
//dir
Vector3f f_r = inter.m->eval(wo, -ws, N);
//indir
Vector3f f_r = obj_to_scene.m->eval(wo, wi, N);
那我们的目光就锁定在eval()这个函数,这个函数是Material.hpp中class Material定义的,用以计算材质返回颜色值。
class Material{
public:
...
//计算光线的贡献
inline Vector3f eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N);
...
}
Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
//计算DIFFUSE贡献 -> kd
float cosalpha = dotProduct(N, wo);//只看半球,另一半不看,所以要判断一下wo和N的夹角
if (cosalpha > 0.0f) {
Vector3f diffuse = Kd / M_PI;
return diffuse;
}
else
return Vector3f(0.0f);
break;
}
}
}
那就先在eval()里定义好MICROFACET这个材质,已经尽量很详细的注释了:
Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
...
}
case MICROFACET:
{
float cosalpha = dotProduct(N, wo);//只看半球,另一半不看,所以要判断一下wo和N的夹角
if (cosalpha > 0.0f) {
//参数的计算
//注意:wi这里是入射光线的方向,因此计算需要变成-wi
float roughness = 0.8;
Vector3f H = (-wi + wo).normalized();
float NdotH = std::max(dotProduct(N, H), 0.0f);
float NdotV = std::max(dotProduct(N, wo), 0.0f);
float NdotL = std::max(dotProduct(N, -wi), 0.0f);
//计算法线密度函数 D
float D = DistributionGGX(NdotH, roughness);
//计算阴影遮挡函数 G
//Disney Way
float G = GeometrySmith_Disney(N, wo, -wi, roughness);
//UE4 way
//float G = GeometrySmith_UE4(N, wo, -wi, roughness);
//菲涅尔项 F
float ior = 1.5;
float F;
fresnel(wi, N, ior, F);
//计算镜面反射的BRDF
float m = 4 * std::max(NdotL * NdotL, 0.00001f);
float Specular = D * G * F / m;
//计算反射和折射光占比
//反射ks
float ks = F;
float kd = 1 - F;
//漫反射的BRDF
float rou = 1.0f;//定义漫反射材质反射率,大小在(0,1),直接给1
float Diffuse = rou / M_PI;
//值得注意的是,这里的Kd和Ks才是对应的颜色,而Specular已经乘过F了(已经考虑了反射光的占比),这里就不用再乘以ks了
return Kd * Diffuse * kd + Ks * Specular;
}
else
return Vector3f(0.0f);
break;
}
}
}
补充计算公式-private:
与上述代码使用到的fresnel()函数相同,我们还需要定义计算G、D值的函数,这些函数跟fresnel()一样,都是在Material类的private里定义的,在private里添加就行。
private:
...
float DistributionGGX(float NdotH, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float m = NdotH * NdotH * (a2 - 1) + 1;
return a2 / std::max(M_PI * m * m, 0.000001f);
}
//定义阴影遮挡函数G
//Disney way:
//对G1的实现
float SmithG_GGX(float NdotV, float roughness) {
float r = 0.5 + roughness / 2.0f;
float m = r * r + (1 - r * r) * NdotV * NdotV;
return 2.0f * NdotV / (NdotV + std::sqrt(m));
}
//光源方向和观察方向分别计算ggx1和ggx2,相乘得到G
float GeometrySmith_Disney(Vector3f N, Vector3f V, Vector3f L, float roughness) {
float NdotV = std::max(dotProduct(N, V), 0.0f);
float NdotL = std::max(dotProduct(N, L), 0.0f);
float ggx1 = SmithG_GGX(NdotL, roughness);
float ggx2 = SmithG_GGX(NdotV, roughness);
return ggx1 * ggx2;
}
//UE4 way
//G1
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = roughness + 1;
float k = r * r / 8;
float m = NdotV / NdotV * (1.f - k) + k;
return NdotV / m;
}
//光源方向和观察方向分别计算ggx1和ggx2,相乘得到G
float GeometrySmith_UE4(Vector3f N, Vector3f V, Vector3f L, float roughness) {
float NdotV = std::max(dotProduct(N, V), 0.0f);
float NdotL = std::max(dotProduct(N, L), 0.0f);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
return ggx1 * ggx2;
}
补充材质剩下的属性-pdf&sample
这里直接贴代码就行,完全是copyDIFFUSE材质的。
Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
switch(m_type){
case DIFFUSE:
{
// 均匀地对半球采样
//半球z轴值z∈[0,1]
// r -> 以法线为旋转轴的半径,x²+y²+z²=1,r²=x²+y²
//phi∈[0,2Π],旋转角度
float x_1 = get_random_float(), x_2 = get_random_float();//随机[0,1]取值
float z = std::fabs(1.0f - 2.0f * x_1);//不是很理解为什么不直接取[0,1]随机数
float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);//半球面上随机光线的方向
//接着需要把半球上的局部光线坐标转换成世界坐标
return toWorld(localRay, N);
break;
}
case MICROFACET:
{
float x_1 = get_random_float(), x_2 = get_random_float();
float z = std::fabs(1.0f - 2.0f * x_1);
float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;
Vector3f localRay(r * std::cos(phi), r * std::sin(phi), z);
return toWorld(localRay, N);
break;
}
}
}
//计算概率密度函数pdf
float Material::pdf(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
switch(m_type){
case DIFFUSE://材质
{
//均匀采样,则pdf为常数1/2Π
if (dotProduct(wo, N) > 0.0f)
return 0.5f / M_PI;
else
return 0.0f;
break;
}
case MICROFACET:
{
if (dotProduct(wo, N) > 0.0f)
return 0.5f / M_PI;
else
return 0.0f;
break;
}
}
}
到这里Material.hpp就结束了,别忘了在开头加上MICROFACET的材质类型。
enum MaterialType { DIFFUSE,MICROFACET};
main.cpp
材质属性&加入物体
这里定义了微表面材质的属性,并向场景里放入了小球,注意,这里的Kd和Ks分别对应颜色。
...
Material*Microfacet = new Material(MICROFACET,Vector3f(0.0f));
Microfacet->Kd = Vector3f(0.5, 0.5, 0.5);
Microfacet->Ks = Vector3f(0.5, 0.5, 0.5);
Sphere sphere(Vector3f(120, 100, 300), 100, Microfacet);
...
//开始划分三角形网格
MeshTriangle floor("../models/cornellbox/floor.obj", white);
//MeshTriangle shortbox("../models/cornellbox/shortbox.obj", white);
//MeshTriangle tallbox("../models/cornellbox/tallbox.obj", white);
MeshTriangle left("../models/cornellbox/left.obj", red);
MeshTriangle right("../models/cornellbox/right.obj", green);
MeshTriangle light_("../models/cornellbox/light.obj", light);
//把物体加入场景中:objects.push_back(object);
scene.Add(&floor);
scene.Add(&sphere);
//scene.Add(&shortbox);
//scene.Add(&tallbox);
scene.Add(&left);
scene.Add(&right);
scene.Add(&light_);
优化部分
到这里并没有完全结束!如果这个时候你直接运行会出现错误:
因此需要给这个函数赋予值,尽管我们用不上他。
Sphere::evalDiffuseColor()
给他一个空值就行。
Vector3f evalDiffuseColor(const Vector2f &st)const {
//return m->getColor();
return {};
}
模型有黑点
这时能正常运行了,但是会出现这样的问题,球的表面会有很多黑点:(spp=4)
这个问题我也是上网搜索到了解决办法,这其实是由于光线和球判断交点的时候误差太大,导致有黑点。具体可以看看这篇文章:
Games101,作业7(微表面模型)_Elsa的迷弟的博客
解决办法是,Sphere::getIntersection()函数里给t0一个判断精度0.5,这个精度也是这篇文章的作者通过不同值的比较得到的:
Intersection getIntersection(Ray ray){
Intersection result;
result.happened = false;
Vector3f L = ray.origin - center;
float a = dotProduct(ray.direction, ray.direction);
float b = 2 * dotProduct(ray.direction, L);
float c = dotProduct(L, L) - radius2;
float t0, t1;
if (!solveQuadratic(a, b, c, t0, t1)) return result;
if (t0 < 0) t0 = t1;
if (t0 < 0) return result;
//给了精度0.5
if (t0 > 0.5) {
result.happened = true;
result.coords = Vector3f(ray.origin + ray.direction * t0);
result.normal = normalize(Vector3f(result.coords - center));
result.m = this->m;
result.obj = this;
result.distance = t0;
}
return result;
}
优化后的结果:(spp=4)
结果:spp=256,用时25min
边栏推荐
- lodash get js代码实现
- Uni app's uparse rich text parsing perfectly parses rich text!
- 2022茶艺师(高级)上岗证题库模拟考试平台操作
- Super détaillé, 20 000 caractères détaillés, mangez à travers es!
- Mmdetection valueerror: need at least one array to concatenate solution
- PAT甲级 1024 Palindromic Number
- pytorch_ grad_ Cam -- visual Library of class activation mapping (CAM) under pytorch
- Geometric distribution (a discrete distribution)
- Learning Tai Chi Maker - mqtt Chapter 2 (II) esp8266 QoS application
- ConstraintLayout(约束布局)开发指南
猜你喜欢
Topolvm: kubernetes local persistence scheme based on LVM, capacity aware, dynamically create PV, and easily use local disk
SQLite reader plug-in tests SQLite syntax
Canvas particles: mouse following JS effect
解决cherry pick提交报错问题
PAT甲级 1020 Tree Traversals
Super detailed, 20000 word detailed explanation, thoroughly understand es!
three. JS domino JS special effect
Pat grade a 1026 table tennis
PAT甲级 1021 Deepest Root
1. Project preparation and creation
随机推荐
How does the brain do arithmetic? Both addition and subtraction methods have special neurons, and the symbol text can activate the same group of cell sub journals
paddlepaddle 19 动态修改模型的最后一层
PAT甲级 1026 Table Tennis
Constraintlayout Development Guide
What if asreml-r does not converge in operation?
Pat grade a 1020 tree Traversals
SQLite reader plug-in tests SQLite syntax
【数组】剑指 Offer II 012. 左右两边子数组的和相等 | 剑指 Offer II 013. 二维子矩阵的和
Learn Tai Chi Maker - mqtt Chapter 2 (3) reserved messages
TP5 spreadsheet excel table export
Enterprise digital transformation: informatization and digitalization
Uninstallation of Dameng database
LeetCode 785:判断二分图
jmeter分布式压测
TechSmith Camtasia latest 2022 detailed function explanation Download
【微服务|Sentinel】降级规则|慢调用比例|异常比例|异常数
three. JS domino JS special effect
bluecms代码审计入门
dat. gui. JS star circle track animation JS special effect
学习太极创客 — MQTT 第二章(三)保留消息