shader多久没用到,自己都忘了。打算写一个自己学习shader的一个过程。还能帮助不懂的新手。
一.先了解opengl的固定流水线—万丈高楼平地起
流水线三部曲:顶点着色器(vs),光栅化,片元着色器(fs)。(光栅化一般新手应该不用管)
三部曲之一 顶点着色器
cpu会把3D模型的各种数据,传给gpu,这个过程,可以简单认为就是dc,dc是一个复杂的东西。但是这里就这样理解。cpu控制gpu的过程。那么合批,就是你让cpu一次性控制传输多个数据,就只花了一个dc。
顶点着色器四大变换:
几何变换:cocos3.x版本,你看node的属性,只有平移,缩放,旋转,因为几何变换,就只有这个,所以node的属性只有这三个。 其中,有个其次坐标的东西。如果你看不懂,就去看B站的Games-101第三节课。 你会了解 变换矩阵怎么来的,齐次坐标是什么,怎么来的。这个不可能简单的文字就能描述。 有了变换矩阵,就可以从本地坐标得到世界坐标,也能从世界坐标得到本地坐标。 你必须知道这个过程,才能知道vs到底是干了啥,看懂为啥会把模型的坐标再叉乘变换矩阵。
这个截图是cocos的standard effect的源码。这就是你需要知道几何变换的原因
投影变换:这玩意数学知识比几何变换难多了,需要继续看game-101第四节课(很难),看不懂也能写shader。这里主要是控制了近,远平面,如何裁剪,正交投影,透视啊。
通过几何变换,我们得到了世界坐标,然后世界坐标*投影矩阵,就可以得到投影坐标
裁剪:一般不用管,opengl都没怎么提供裁剪的api,理解为得到摄像机看到的部分。
投影坐标*裁剪矩阵,就得到裁剪坐标
视口变换:这个应该是为了封装适配硬件,显示器的长宽比例不一样,这里就通过视口变换。
四大变换看了估计还是一脸懵逼。
懵逼就直接看代码
这是coco3.x无光照的vs-effect,包含了主要的源码,基本上,顶点着色器,就是处理顶点的坐标
这里代码做的事情,就是在 把本地坐标 变成裁剪后的 坐标
总结一下,顶点着色器干的事情
从模型空间(本地坐标) 得到了裁剪坐标。 至于后续再变成ndc坐标,最后成为屏幕上看到,从源码上看,就不是顶点着色器该干的事情。
//----------
上面看得到的代码是无光照代码。
那么如何得到光照呢?
在固定流水线里,因为在计算顶点的坐标,法线,所以直接在顶点着色器里计算量光照。
因为光照必须要用到法线数据(因为光要照在一个平面上,那肯定要知道平面的方向,而法线就是平面的方向),所以传统的固定流水线,光照计算就在vs里面进行。
光照 = 环境光 + 漫反射 + 镜面反射 。
由于这里只简单讲,复杂了,用文字你也看不懂。
我的总结:光 ,就是一种颜色; 假设是红色 。 光照,就是通过法线,摄像机角度等,给每个顶点“加权平均”这个颜色,至于这个加权平均的过程,你需要自己去看光照模型
自我总结的一个简单光照明模型(现在的standard光照模型,比这个难多了)
现在的shader,光照都放在了片元着色器里,因为用到的数据都在fs里面。
有哪些数据呢,比如法线贴图,为啥要用法线贴图,模型的凹凸感,是光照的结果,那么凹凸感是需要计算法线的的,这个过程很麻烦。最重要的是,你的模型必须是凹凸的,才能计算出来法线->从而计算光照。那么模型的面数就非常多,面数多就会卡。于是有了法线贴图。
法线贴图告诉gpu:你不要去计算模型的法线,你直接用我给你的法线数据。于是一个没有凹凸感的模型,也能显示凹凸感。
看代码,正常情况,我们是通过模型计算法线,而这里,USE_NORMAL_MAP
就是告诉gpu,我使用法线贴图,你就不用去计算法线了。直接读取我们的法线数据,拿去计算光照
//-----------------------------------------------顶点着色器总结--------------------------------------------//
在无光照的情况下,这小子就干了一件事情:把本地坐标->裁剪坐标
通过坐标*各种矩阵得到
三部曲之二:光栅化
光栅化要处理的东西:计算顶点占据了哪些像素,顶点内的像素颜色。在vs的时候计算的是顶点的光照颜色,但是一个面片有多个像素,像素的颜色并没有计算。光栅化一般就去计算哪些像素属于这个面片内,哪些是面片外。这都是自动处理的,不管。
暂时就这样就行了。
三部曲之三:片元着色器 fs (三大缓冲区 + 四大测试 )
三大缓冲区:
在fs里,会对每一个像素进行处理。所以我们shader大多情况都在写fs
首先,所有的数据,都会放入一个缓存—帧缓存吧
你的显示器,每次刷新界面(一秒钟60次),每次都是从这个帧缓存区取到数据
帧缓存又细分:颜色缓冲区(rgba) , 深度缓冲区 , 模版缓冲区
颜色缓冲区:这个好理解,一个图片的颜色都在这里了。所以:一张图片所占据的内存怎么计算? 一个图片 1024 * 1024 ,假设一个r通道,用8位来存颜色,所以2的8次方 = 255 。 你平时用的cocos,一个图片的颜色通道,填写的就是0-255,这就是255的来源,2的8次方。
光一个通道8位,那么4个通道就是 32位。 一个字节是8位,所以4个通道就是 4个字节
所以一个1024 x 1024 的图片,大小 = 1024 x 1024 x 4 个字节 ,而 1024 = 1k , 1m = 1024 k,所以这张图片的大小就是4m。 这个4m就必须让在gpu的显存中。
深度缓冲区:3d世界里,因为有前后的物体,前面的物体,会挡住后面的物体,所以gpu怎么知道,到底要显示谁呢,这就需要深度缓冲区,来存储颜色的深度(z-buffer算法)
因为有gpu是从缓冲区拿数据,所以每次都应该清除。当你用cocos的camera的时候,有个选项,清除颜色,还是清除深度,就是对应的控制这两个缓冲区,如果不清除,就会叠加。
模版缓冲区:这个咋理解呢,功能像 Mask,就是在 模版内的颜色,才会渲染,模版外的颜色,是不会渲染的。作用就是用来过滤的。
四大测试:i
gpu显示东西,从缓冲区拿数据,但是我们数据放入缓冲区之前,要进行测试,通过了才能放进去
直接看图把,模版测试和裁剪测试我也说不清楚,alpha测试现在只在shader手动操作
alpha测试,就是rgba的a通道,透明度,如果你的透明度低于设置的值,就不会通过测试
直接看源码
源码 if (baseColor.ALPHA_TEST_CHANNEL < albedoScaleAndCutoff.w) discard;
低于设定,直接discard,discard就是gpu就不绘制它了。
深度测试就是,颜色有个“Z‘值,低于这个值的也不会显示,这些理论,不会让你直接就能写shader,但是能帮助你看懂这个shader,为啥这里要加这些代码
再补一个 透明度 ,透明和不透明等之间重叠,颜色是如何显示的
好了,简单的一个opengl 固定的渲染流水线就这样,目前这个过程,是为了看shader,哪一行代码,为啥出现在这里,是这个流水线哪个部分的过程。
//---------------------------------------fs总结
fs对每一个像素进行处理,经过四大测试,三个缓冲区。
后续讲讲简单的 effct 怎么写
后续等更新
ps:有错误的地方,欢迎指出。
//-----------------------------------------------------------------------------------------------------------------
数学知识补充
//-------------------------------------------------------3.8.x shader编写----------------------------------------------------
2D的描边
//我的设计:什么是边缘?就是 自己这个点是透明的,而“邻居”不是透明的,说明自己是 “边缘”,是边缘就给自己设置成边缘的颜色
上代码
加几个常量
然后获取“邻居的顶点”,这里只获取上下左右,也可以再加入4个点(上左,上右,,,,)

最fs里面判断一下
最终效果
加入8个点应该效果更好,还有就是这个
完成代码看仓库https://gitee.com/xiao_shuai1973/demo-free













