当前位置:网站首页>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

边栏推荐
- PAT甲级 1018 Public Bike Management
- Super detailed, 20000 word detailed explanation, thoroughly understand es!
- dat. gui. JS star circle track animation JS special effect
- Laravel 的 ORM 缓存包
- 元透实盘周记20220627
- Mmdetection uses yolox to train its own coco data set
- The use and introduction of pytorch 23 hook and the implementation of plug and play dropblock based on hook
- 一文教你Kali信息收集
- ConstraintLayout(约束布局)开发指南
- Summer planning for the long river
猜你喜欢

jmeter将上一个请求的结果作为下一个请求的参数

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

Pat grade a 1023 have fun with numbers

Introduction to stm32

Record the method of reading excel provided by unity and the solution to some pits encountered

SQLite reader plug-in tests SQLite syntax

Cs5213 HDMI to VGA (with audio) single turn scheme, cs5213 HDMI to VGA (with audio) IC

Dameng database installation

Learn Tai Chi maker mqtt (IX) esp8266 subscribe to and publish mqtt messages at the same time

ConstraintLayout(约束布局)开发指南
随机推荐
学习太极创客 — MQTT(七)MQTT 主题进阶
servlet与JSP期末复习考点梳理 42问42答
人群模拟
超級詳細,2 萬字詳解,吃透 ES!
1、项目准备与新建
Topolvm: kubernetes local persistence scheme based on LVM, capacity aware, dynamically create PV, and easily use local disk
Paddlepaddle 19 dynamically modify the last layer of the model
Learning Tai Chi Maker - mqtt (VII) advanced mqtt theme
Paddlepaddle 21 is implemented based on dropout with 4 lines of code droplock
2021:Greedy Gradient Ensemble for Robust Visual Question Answering
paddlepaddle 20 指数移动平均(ExponentialMovingAverage,EMA)的实现与使用(支持静态图与动态图)
2022中式面点师(高级)复训题库及在线模拟考试
Paddlepaddle 20 implementation and use of exponentialmovingaverage (EMA) (support static graph and dynamic graph)
jwt的认证流程和使用案例
Google began to roll itself, AI architecture pathways was blessed, and 20billion generation models were launched
Microsoft365开发人员申请
Questions and answers of chlor alkali electrolysis process in 2022
Summer planning for the long river
Uninstallation of Dameng database
学习太极创客 — MQTT 第二章(一)QoS 服务质量等级