【ShaderToy】基础篇之谈谈点、线的绘制

写在前面

写前面一篇的时候,发现还是不够基础。因此打算增加几篇基础篇,从点线面开始,希望可以更好理解。

其实用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) 难懂的是将得到在屏幕中归一化后的屏幕位置,即返回分量范围在(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中,一个点其实可以当成一个圆形。那么问题就变成了如何绘制一个圆:给定圆心在屏幕的位置(圆心像素值占屏幕的百分比),圆的半径(像素为单位),以及圆的颜色,如何绘制一个圆。

为此,我们先需要在Properties中声明两个参数:_Parameters和_Color:

Shader "shadertoy/Simple Circle" { Properties{_Parameters ("Circle Parameters", Vector) = (0.5, 0.5, 10, 0) // Center: (x, y), Radius: z_Color ("Circle Color", Color) = (1, 1, 1, 1)}_Parameters的x和y分量表示圆心在屏幕中的uv值(即范围在(0, 1)),z分量是圆的半径,单位是像素。

圆在数学里面的表达式相对简单,即到圆心距离小于半径的点就在圆内。那么事情就变得简单了:只要在圆内的点就是用设置的颜色绘制,否则用背景色绘制(黑色)。

vec4 circle(vec2 pos, vec2 center, float radius, float4 color) {if (length(pos – center) < radius) {// In the circlereturn vec4(1, 1, 1, 1) * color;} else {return vec4(0, 0, 0, 1);}}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 circle(pos, _Parameters.xy * iResolution.xy, _Parameters.z, _Color);}得到的效果如下:

我不要锯齿!

擒龙要下海,打虎要上山。

【ShaderToy】基础篇之谈谈点、线的绘制

相关文章:

你感兴趣的文章:

标签云: