写在前面
写前面一篇的时候,发现还是不够基础。因此打算增加几篇基础篇,从点线面开始,希望可以更好理解。
其实用Pixel Shader的过程很像在纸上绘画的过程。屏幕上的每一个像素对应了纸上的一个方格,如果你愿意,你甚至可以一个个判断像素的位置,从而画出任何你想画的图像,也的确有爱好者这么做过。但往往,我们需要的是一个动态的效果,这个效果往往依赖于数学公式的约束。我们可以说是,用数学去绘画。我们用数学去约束,哪些点应该用什么颜色去绘制。
这篇,我们从基本的点和线开始,看一下如何在Pixel Shader里面随心画出点和线。
在哪里画
在开始之前,有一个最基本的问题我们要计算清楚,就是如何知道当前计算的fragment的像素位置。在之前的开篇中,我们给出了模板。其中v2f结构里,有一个很重要的变量srcPos,它的计算如下:
v2f vert(appdata_base v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.srcPos = ComputeScreenPos(o.pos);
o.w = o.pos.w;
return o;
}
ComputeScreenPos是在UnityCG.cginc中定义的函数,它就作用如名字一样,计算该顶点转换到屏幕上的位置。但如果我们想要得到正确的屏幕位置,还需要在frag函数中这样:
fixed4 frag(v2f _iParam) : COLOR0 {
vec2 fragCoord = gl_FragCoord;
return main(gl_FragCoord);
}
其中:
// 屏幕的尺寸
#define iResolution _ScreenParams
// 屏幕中的坐标,以pixel为单位
#define gl_FragCoord ((_iParam.srcPos.xy/_iParam.srcPos.w)*_ScreenParams.xy)
难懂的是 gl_FragCoord的定义。 (_iParam.srcPos.xy/_iParam.srcPos.w)将 得到在屏幕中归一化后的屏幕位置,即返回分量范围在(0, 1)的屏幕横纵坐标值。 屏幕的左下角值为(0, 0),右上角值为(1, 1)。然后再乘以屏幕的长款像素值,就得到了该fragment对应的屏幕像素位置。这是我们后面计算的基础。
根据不同的需求,我们会在shader中对位置有不同的需求:有时我们想要得到如上的像素位置,有时我们想得到相对于屏幕中心的uv坐标等等。以下有五种常见的位置需求:
vec4 main(vec2 fragCoord) {
vec2 pos = fragCoord; // pos.x ~ (0, iResolution.x), pos.y ~ (0, iResolution.y)
vec2 pos = fragCoord.xy / iResolution.xy; // pos.x ~ (0, 1), pos.y ~ (0, 1)
vec2 pos = fragCoord / min(iResolution.x, iResolution.y); // If iResolution.x > iResolution.y, pos.x ~ (0, 1.xx), pos.y ~ (0, 1)
vec2 pos =fragCoord.xy / iResolution.xy * 2. - 1.; // pos.x ~ (-1, 1), pos.y ~ (-1, 1)
vec2 pos = (2.0*fragCoord.xy-iResolution.xy)/min(iResolution.x,iResolution.y); // If iResolution.x > iResolution.y, pos.x ~ (-1.xx, 1.xx), pos.y ~ (-1, 1)
return vec4(1);
}
当然需求不同,一开始计算的pos也会不同。
画第一个点(圆)
在Shader中,一个点其实可以当成一个圆形。那么问题就变成了如何绘制一个圆:给定圆心在屏幕的位置(圆心像素值占屏幕的百分比),圆的半径(像素为单位),以及圆的颜色,如何绘制一个圆。
为此,我们先需要在