着色
着色 shading
对于每一个shading point
赋予每个像素点:明暗或者颜色
对一个物体添加材质的过程
着色过程是只考虑单独物体,不考虑其他物体造成的光线遮挡,所以着色完成后之后有明暗,颜色,不会有阴影
考虑局部
shading point所在的平面的法线与光线的夹角(法线向量与光线向量的夹角)导致了这个表面的明暗变化
Blinn-Phone反射模型:漫反射+高光+环境光照
漫反射
光在漫反射的平面上反射后是均匀的,观察效果与观察位置无关
光源到反射平面的能量在平面上的作用后
Lambertian(Diffuse)Shading
Ld:人眼接受的漫反射光 diffusely reflected light
kd:diffuse coefficient(color) 着色点的吸收率
max(0,n·l):着色点能接受到的能量
I/r²:光到达着色点的能量,
点光源能量守恒,在一个球体内,每个球面的能量相同
高光 specular
高光产生原因:对于一个shading point 镜面反射方向与观察方向足够接近的时候会表现出高光。
为了求解高光的数学表达,引入h向量,该向量为入射向量和观测向量的角平分线,其与shading point的法线方向的夹角,就是镜面反射向量与观测向量的夹角。由于其数学表达式易求。
注意到这个角度有一个指数,添加这个指数的原因是为了使得高光更加贴合实际,比如如果角度为45°,应该没有什么高光效果,但其余弦仍然是一个较大的值。
环境光照#
假设任何一个点从各处环境接受的光照是相同的
与入射方向和观测方向无关,一般为一个常数
Blinn Phone模型
观测点对shading point的距离是不会影响效果,而光源对shading point的距离会影响效果
着色频率
对所有shading point 进行着色
三种处理方式
-
Flat shading
将物体分为多个平面,如果将这个平面视为shading point进行shading得到的着色效果
-
Gouraud shading
对每个平面的顶点当作shading point进行着色,然后对平面内的点进行插值计算颜色
-
Phone shading
对平面内每一个像素当作shading point进行着色,其法线方向主要是通过平面顶点的法线插值得到,从而进行着色
求取顶点的法线#
- 当目标模型是一个圆或者球时,顶点的法线就是圆心(球心)与该点的连线的单位向量
- 对于一般的目标模型,一个顶点一般为多个三角形(划分的平面)的顶点,可以求出每个三角形平面的法线方向,然后加权平均得到该顶点的法线
每个像素的法线#
已知顶点求取两点中间的各点法线,利用重心坐标
渲染(实时渲染)管线
管线pipeline:从场景到图片的过程
输入空间的点->投影->点形成三角形->光栅化形成像素->对像素着色->最终屏幕上的图片颜色
Shader->控制顶点或者像素是如何着色的代码
- 一些管线步骤下的具体操作:
vetex processing: MVP变换,gouraud shading,纹理(贴图Texture mapping)
rasterization: 采样,反走样
fragment processing: Z-buffer,phone shading,纹理(贴图Texture mapping)
Shader Programs#
GPU允许自己编程控制顶点或者像素如何着色,这个shader应该是通用的,不需要调用for循环。下面以一个opengl的简洁版对像素点只用漫反射的着色举例
//全局变量
uniform sampler2D myTexture;
uniform vec3 lightDir;
//定义法线
varing vec3 norm;
//uv为纹理上的坐标
varing vec2 uv;
//每个像素如何着色
void diffuseShader(){
//计算漫反射系数
vec3 kd;
//与纹理相关
kd = texture2d(myTextur,uv);
//与入射光线和法线的夹角余弦相乘,其余两个变量均为1
kd *= clamp(dot(-lightDir, norm),0.0,1.0);
//返回到opengl的变量中输出该像素的颜色
gl_FragColor = vec4(kd,1.0);
}
纹理Texture Mapping
作用:定义物体上任何一个点的特有的属性
纹理为一张二维图,贴图。
将一个三维物体展开为二维物体,并且使得每一个划分的三角形与二维贴图上的三角形一一对应,这样就获得了三维图形的每一个三角形的纹理就是它的属性。
二维贴图一般的范围是1×1的正方形大小。每一个三角形在纹理上对应了一个坐标uv。良好的纹理是可以复用的,并且前后左右相互可以完美拼接。
插值
求得三角形顶点值后,如何求取平滑的内部值,使用重心坐标,计算三角形内的点与顶点关系,然后将这个关系应用到其他情况(比如纹理,深度等)
一个与三角形同平面的点可以这样表示
如果
有两种方法来计算
第二种是利用坐标,其实就是将叉积面积求解展开
接下来就要利用
注意:重心坐标在投影变换后会发生改变,对于深度的插值,所使用的重心坐标应当是投影之前在三维空间的重心坐标。
理想情况下,对于每一个像素点恰好有一个对应的纹理,我们只需要把纹理坐标(u,v)的值赋值给像素坐标(x,y)就可以了,但实际情况比较复杂,主要有两种情况:第一种为纹理图案的尺寸比整个屏幕小,第二种情况就是纹理图案的尺寸比整个屏幕大
为了方便处理,我们将纹理也分成块状称为texel
纹理图案小的情况
不经过处理,可能会导致多个像素pixel使用同一个texel,图像锯齿状严重。
一种简单的处理方式为Bilinear,双线性插值,对于下方这样一个pixel中心点(红点),在texel上的位置,如果不处理,那么这个texel所包含的所用pixel中心点都会被赋以相同的颜色,而为了求取相对准确的pixel,利用将它围住的4个texel做双线性插值。
插值相关公式
最终坐标就是
纹理图案较大的情况
具体表现为在近处,因为一个像素无法包含一个纹理,导致锯齿状,在远处一个像素包含了多个纹理,而最终表现仅仅是区域内中间纹理的值,导致摩尔纹的出现
可以发现实际上就是采样问题,只不过之前是物体投影在pixel上被采样,现在是pixel在texel上被采样,其实也是物体投影,因为物体投影经过光栅化后,就通过pixels来组成了。
如果利用超分辨率,是可以解决的,但是消耗过大,超分辨其实就是利用将一个pixel分成多个子Pixel,然后子pixel去寻找合适的texel然后平均。那么有没有一种方法可以直接获得一个区域的平均值呢?
那就是范围查询,范围查询和点查询都是属于查询类算法,点查询就如同之前的双线性插值,根据多个点求解一个点的值。而范围查询就是查找这个范围的平均值,最大值,最小值等。
我们现在主要利用mipmap来求解平均值--fast approx.(not accurate) only square
mipmap#
除去原始的第一张,剩余缩小的图加起来所需的多余容量为1/3,然后mimap相当于一个散列表,将每次的图片存储起来,索引值为层数。原始图片为0.
如何使用mipmap在纹理映射中#
上图左边为光栅化图形,uv为纹理坐标,右边为像素点映射到纹理坐标上的结果,我们利用Mipmap求取的就是图中红点和蓝点的纹理值。那么如何确定该像素点在纹理图上的覆盖区域呢。
使用红点举例,左下角的红点,在光栅化图上有上和右两个红点,投影在纹理图上也是相似位置,然后求取在纹理图上上点与该点的距离,右点与该点的距离,选择最大值,成为该点的square边长
求取这个正方形边长后,就需要根据这个正方形的边长L,在mipmap中查询平均值,平均值就是当Mipmap的square变为1×1时。而什么时候变成1×1呢,层数
例如,当L是包含了4×4个texel时,通过Mipmap,第一次之后变为2×2,第二次变为1×1,此时的mipmap图像就是该边长为L的square的平均值。
回到实际中,当物体离我们比较近,一个square的包含的可能就是1×1的texel不需要平均,我们看到也比较清晰,而当离我们比较远,一个square包含的可能就是n×n的texel通过mipmap,就显得模糊。那么随之而来的又有两个问题,第一个是变换不连续,第二个是远处会非常模糊。
- 解决不连续问题:利用插值
不连续问题的原因:我们的层数是整数分散的,0,1,2,...但图片是连续的。
解决:比如求解1.8层,首先在光栅化上每个点在每一层纹理投影上都有自己的位置,我们对代求的光栅化上的一个点它的实际mipmap层数是1.8层,那么我们通过双线性插值,求得他在第1层和第2层上的值,然后再利用这两个值,在层与层之间插值
该图为光栅化图,不是纹理uv图,图中黑点表示pixel的中心点,红点表示任意非pixel中心点的点
主要原因:远处一个像素实际投影在uv图上的为一个细长的斜的长方形,导致其所需要的square的L较大,使得其模糊过度。
解决方法:
mipmap的作用实际是对角线,相当于增加分析情况(pixel),来避免模糊,可查询的内容更多
内存开销是原本的3倍。
2. EWA :对于斜着的长方形
将每个图形分割为多个圆形,然后每次查询都是多次查询
作者:XTG111
出处:https://www.cnblogs.com/XTG111/p/17738862.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律