着色2
着色
模型-投影变换:物体变为标准立方体中
视口变换:3D物体变为2D平面
光栅化:绘制物体在2D平面上
着色:对物体应用材质 -- apply a material to an object
材质在光照作用下表现出来的颜色
Bling-Phone模型光照会出现的情况:漫反射 + 高光 + 环境光照
Shading Point
考虑的光照最简模型。一个接受光照的平面,其主要有4个参数
法线:n
观测方向:v
光照方向:l
表面属性:(材质)
着色的局部性 着色不会产生阴影,他只考虑物体自己,当在考虑时,会在空间中去掉其他物体
漫反射
光照方向与Shading Point 法线的夹角余弦
点光源的能量在距离上的衰减
点光源周围可以看作为一圈一圈的等势线,距离光源r的点能够接受到的能量为
Lambertian Diffuse Shading -- 漫反射的计算结果
可以看出在漫反射计算公式中,并没有考虑观测向量v对结果的影响,因为对于漫反射来说从四面八方看到的结果应该是一样的
高光
观测方向接近镜面反射方向
光照方向在Shading Point 上存在一个镜面反射向量,当该向量与观测方向接近时,将表现出高光
等价于
Shading Point的法线向量与入射向量和观测向量的半程向量(两个向量组成的角的角平分线)接近
p次幂让高光范围减小
环境光照
假设任何点接受到的环境光照是相同的
Blinn-Phone 着色模型
着色频率
flat shading
利用一个三角形面的法线来计算着色
Gouraud Shading
利用一个三角形的三个顶点法线计算顶点颜色,然后插值计算三角形内部的颜色
for(每个三角形)
{
color P0;
color P1;
color P2;
for(三角形中的每个像素)
{
//计算该像素在三角形中的重心坐标
color pixelc = P0*x + P1*y + P2*z;
setcolor(pixel,pixelc);
}
}
根据三角形面求解顶点法线
一个顶点会被多个三角形面共用,所以该顶点法线就为 每个面法线的平均
Phone Shading
对每个像素通过三角形的三个顶点插值计算法线,然后利用该法线计算每个像素的颜色
for(每个三角形)
{
normal P0;
normal P1;
normal P2;
for(三角形中的每个像素)
{
//计算该像素在三角形中的重心坐标
normal pixeln = P0*x + P1*y + P2*z;
setcolor(pixel,getcolor(pixeln));
}
}
实时渲染管线
- 输入3D空间中的点
- MVP变换 + 视口变换 3D点变到2D平面上
- 点连接为三角形 obj文件有相应的定义
- 光栅化--离散三角形为像素块
- 对像素块着色
- 输出
纹理映射
定义任何一个点的属性,一般存在一个纹理贴图
以obj文件来说,obj文件内可能会定义给出每个顶点的uv坐标,可以通过读取uv坐标值,从而在纹理贴图中寻找到该点的属性
一般来说,纹理贴图的坐标是由RGB来表示的即其坐标范围在(0,255)之间,而经过变换后的物体坐标在(-1,1)之间,所以为了对应,需要对纹理坐标进行处理
此时再根据之前求得的重心坐标,利用三角形的三个顶点求得每个像素点的纹理值,即可
重心坐标
用于插值计算。对于与一个三角形处于同一平面的点(x,y)都可以由三角形的三个顶点线性组合得到
对于在三角形内部的一点,
求解重心坐标
//_v为三角形三个顶点组成的数组,x,y为点
Vector3f a (_v[1].x()-_v[0].x(),_v[2].x()-_v[0].x(),_v[0].x()-x);
Vector3f b (_v[1].y()-_v[0].y(),_v[2].y()-_v[0].y(),_v[0].y()-y);
//叉乘
Vector3f c (a.y()*b.z()-a.z()*b.y(),a.z()*b.x()-a.x()*b.z(),a.x()*b.y()-a.y()*b.x());
//点(x,y)的重心坐标
Vector3f res (1.f-(c.x()+c.y())/c.z(),c.y()/c.z(),c.x()/c.z());
重心坐标的应用
对于三角形内部任何一个点的属性(颜色,法线等)都可以通过重心坐标与三角形的三个顶点对应属性相乘得到
重心坐标在投影变换下的属性会发生改变,对于待渲染物体的重心坐标,应当在3D空间中求得
如何应用纹理
for(每一个像素)
{
//根据插值计算出该像素的纹理坐标u,v
(u,v) = cal(pixel);
//根据uv在贴图上查询颜色
color = get_(u,v);
//设置该像素的颜色
set_(pixel,color);
}
纹理贴图过小-- 放大
纹理贴图较小 --> 拉伸纹理贴图至物体图像大小
--> 一个texel 对应 多个pixel --> 导致一个pixel在求取颜色时,(u,v)坐标不是整数 --> 而采用四舍五入的方法,导致图像精度的降低
解决办法:双线性插值
利用包围该pixel的4个texel进行插值求解颜色。相当于是二维平面的重心坐标。假设有当前pixel坐标为
//小于x0,y0的最大整数, 大于x0,y0的最小整数
int minx;
int miny;
int maxx;
int many;
float s = x0-minx;
float t = y0-miny;
//对于在x方向可以插值
color c1 = lerp(s,cminx_miny,cmaxx_miny);
color c2 = lerp(s,cminx_maxy,cmaxx_maxy);
//然后利用c1 和 c2 在 y方向上插值
color res = lerp(t,c1,c2);
纹理贴图过大 -- 压缩
导致一个pixel 对应 多个 texel -> pixel中心求得的颜色不能代替多个texel的颜色
解决办法1:MSAA
将一个pixel划分为多个采样块
解决办法2:MipMap
将点查询变为范围查询,MipMap -- 快 不准确,方形
原理:将纹理先行处理,即不断降低1倍的分辨率,存储纹理值,得到
如何确定选择哪一张图片
对于pixel p0其上方和右方有pixel p1和p2,三点映射到纹理贴图上,可以求得u1u0 和 u2u0的距离两者距离最大值L,选择
比如当前L=4说明当前映射覆盖了4×4的texel,需要经过2次降低,才会得到1块texel,那么选择该mipmap即为平均值。
对于非整数层的MipMap
通过双线性插值求解
首先在上下两个整数层求解对于pixel的纹理,然后再通过层与层之间的重心坐标求解非整数层的纹理
MipMap问题
过度模糊
- 解决方法:各向异性过滤
Mipmap是纹理长宽同比缩小,考虑一个矩阵,相当于只存储了对角线的元素,而再添加其余的缩小样本,将解决只能处理正方形区域的问题。
但仍然不能解决斜着的映射图像。
凹凸贴图 Bump Texture
对法线的扰动实现渲染出凹凸的现象。凹凸贴图存储的是每个pixel的高度信息,使得可以通过相邻两个pixel的高度差计算得到当前pixel的法线
考虑2D平面
原始点P的法线n(p) = (0,1)
对于点P在凹凸贴图上可以求得其切线 dp = c*[h(p+1)-h(p)]
利用该条切线求得凹凸贴图影响下的 法线变换 n(p) = (-dp,1).normal;
考虑3D空间
原始点P的法线n(p) = (0,0,1)
切线有2个方向:u,v
然后再根据该切线求得P点的法线 n(p) =
真实的法线
定义的原始法线都为(0,0,1),而实际法线并不是这个数值
在一个微元上,可以认为法线是(0,0,1)在求得由凹凸贴图更改的法线后,再利用原始法线的向量值进行矩阵变换求得真实改动后的法线
位移贴图 Displayment Texture
实际改变点的位置
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了