[ShaderToy] This topic describes how to draw points and lines and how to draw shadertoy basics.

Source: Internet
Author: User

[ShaderToy] This topic describes how to draw points and lines and how to draw shadertoy basics.


Preface


When I wrote the previous article, I found that it was not enough. Therefore, I plan to add several basic articles, starting from the point and line, to better understand them.


In fact, the process of using Pixel Shader is similar to the process of painting on paper. Each pixel on the screen corresponds to a square on the paper. If you want to, you can even determine the position of the pixel one by one to draw any image you want to draw, some enthusiasts have done this. However, we often need a dynamic effect, which is often dependent on the constraints of mathematical formulas. We can say that,Draw with mathematics. We use mathematics to constrain which points should be drawn with what color.


This article describes how to draw points and lines in Pixel Shader from the basic points and lines.



Where to draw


Before we start, we need to calculate the most basic problem, that is, how to know the pixel location of the currently calculated fragment. In the previous section, we provided the template. In v2f structure, srcPos is a very important variable. Its calculation is as follows:

        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 is a function defined in UnityCG. cginc. It acts like a name to calculate the position where the vertex is converted to the screen. But if we want to get the correct screen location, we also need to do this in the frag function:

        fixed4 frag(v2f _iParam) : COLOR0 {         vec2 fragCoord = gl_FragCoord;        return main(gl_FragCoord);        }  

Where:

// Screen size # define iResolution _ ScreenParams // coordinates in the screen, in pixel # define gl_FragCoord (_ iParam. srcPos. xy/_ iParam. srcPos. w) * _ ScreenParams. xy)

What is hard to understand is the definition of gl_FragCoord. (_ IParam. srcPos. xy/_ iParam. srcPos. w) Obtain the normalized screen position on the screen.Returns the x-axis and Y-axis values of the screen in the range of (0, 1. The value in the lower left corner of the screen is (0, 0), and the value in the upper right corner is (1, 1). Then multiply the long pixel value of the screen to obtain the pixel position of the screen corresponding to the fragment. This is the basis of our subsequent computing.


Based on different requirements, we have different locations in the shader: Sometimes we want to get the above pixel location, sometimes we want to get uv coordinates relative to the center of the screen, and so on. There are five common location requirements:


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);}

Of course the requirements are different, and the pos calculated at the beginning will also be different.



Draw the first vertex (circle)


In the Shader, a vertex can actually be regarded as a circle. The problem is how to draw a circle: the position of the given center on the screen (the percentage of the center pixel value to the screen), the circle radius (in pixels), and the circle color, how to draw a circle.


Therefore, we need to declare two Parameters in Properties: _ Parameters and _ 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)}

The x and y components of _ Parameters indicate the uv values of the center in the screen (that is, the range is (0, 1), and the z component indicates the radius of the circle, in pixels.


The expression of the circle in mathematics is relatively simple, that is, the point with a distance less than the radius from the center of the circle is inside the circle. This makes things easy: as long as the point in the circle is drawn with the set color, otherwise it is drawn with the background color (black ).

        vec4 circle(vec2 pos, vec2 center, float radius, float4 color) {        if (length(pos - center) < radius) {        // In the circle        return 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);}

The result is as follows:

 


I don't want to see it!


The circle obtained above has some small sertices at the edge, which of course cannot be pulled! The anti-aliasing principle in the Shader is probably like this: Because the Calculation of Non-A is B, the junction of A and B will produce A sawtooth (such as the boundary of the circle above ), therefore, we only need to smooth the transition between A and B. This usually requires the combination of transparency, that is, using transparency to mix colors.


In the shader, a common anti-sawtooth (smooth) operation is to use the smoothstep function (Of course someone criticized this method for being not intuitive, but I think it is quite helpful... Whatever ~). The smoothstep function is shown in the CG document as follows:

Interpolates smoothly from 0 to 1 based on x compared to a and b.1) Returns 0 if x < a < b or x > a > b1) Returns 1 if x < b < a or x > b > a3) Returns a value in the range [0,1] for the domain [a,b].

That is to say, its return value range is always (0, 1), that is, the transparency value range, which is why it is so popular in the anti-aliasing field.


In this way, we can rewrite the original circle function:

        vec4 circle(vec2 pos, vec2 center, float radius, float3 color, float antialias) {        float d = length(pos - center) - radius;        float t = smoothstep(0, antialias, d);        return vec4(color, 1.0 - t);        }

Antialias is the boundary range of smooth transition. To facilitate debugging, we can use the z component of _ Parameters in the shader as the anti-sawtooth factor. Of course, we can set the value in actual engineering. The next step is to mix with the background color. We use the lerp function (in ShaderToy, the mix function is used ):


vec4 main(vec2 fragCoord) {vec2 pos = fragCoord; // pos.x ~ (0, iResolution.x), pos.y ~ (0, iResolution.y)vec4 layer1 = vec4(_BackgroundColor.rgb, 1.0);vec4 layer2 = circle(pos, _Parameters.xy * iResolution.xy, _Parameters.z, _CircleColor.rgb, _Parameters.w);return mix(layer1, layer2, layer2.a);}

The complete code is as follows:

Shader "shadertoy/Simple Circle" {Properties {_ Parameters ("Circle Parameters", Vector) = (0.5, 0.5, 10, 1) // Center: (x, y ), radius: z_CircleColor ("Circle Color", Color) = (1, 1, 1, 1) _ BackgroundColor ("Background Color", Color) = (1, 1, 1, 1)} CGINCLUDE # include "UnityCG. cginc "# maid target 3.0 # define vec2 float2 # define vec3 float3 # define vec4 float4 # define mat2 float2x2 # define iGlobalTi Me _ Time. y # define mod fmod # define mix lerp # define atan atan2 # define fract frac # define texture2D tex2D // screen size # define iResolution _ ScreenParams // coordinates on the screen, in pixel # define gl_FragCoord (_ iParam. srcPos. xy/_ iParam. srcPos. w) * _ ScreenParams. xy) # define PI2 6.28318530718 # define pi 3.14159265358979 # define halfpi (pi * 0.5) # define oneoverpi (1.0/pi) float4 _ Parameters; float4 _ CircleColor; flo At4 _ BackgroundColor; struct v2f {float4 pos: SV_POSITION; float4 srcPos: TEXCOORD0 ;}; // precision highp float; v2f vert (appdata_base v) {v2f o; o. pos = mul (UNITY_MATRIX_MVP, v. vertex); o. srcPos = ComputeScreenPos (o. pos); return o;} vec4 main (vec2 fragCoord); fixed4 frag (v2f _ iParam): COLOR0 {vec2 fragCoord = gl_FragCoord; return main (gl_FragCoord );} vec4 circle (vec2 pos, vec2 center, f Loat radius, float3 color, float antialias) {float d = length (pos-center)-radius; float t = smoothstep (0, antialias, d); return vec4 (color, 1.0-t);} vec4 main (vec2 fragCoord) {vec2 pos = fragCoord; // pos. x ~ (0, iResolution. x), pos. y ~ (0, iResolution. y) vec4 layer1 = vec4 (_ BackgroundColor. rgb, 1.0); vec4 layer2 = circle (pos, _ Parameters. xy * iResolution. xy, _ Parameters. z, _ CircleColor. rgb, _ Parameters. w); return mix (layer1, layer2, layer2.a);} ENDCG SubShader {Pass {CGPROGRAM # pragma vertex vert # pragma fragment frag # pragma fragmentoption implements ENDCG} FallBack Off}

The anti-aliasing effect is as follows:

 

 



Draw two points


Now let's take a look at how to draw more points. The previous circle function can draw a circle of any size and circle center. The question now is how to add these elements to the canvas. A basic idea is layer superposition, which is similar to what we do in Photoshop: In the last layer of the background, we only need to add new layers, and make sure they are arranged in a hierarchical order. Therefore, we can do this:

vec4 main(vec2 fragCoord) {vec2 pos = fragCoord; // pos.x ~ (0, iResolution.x), pos.y ~ (0, iResolution.y)vec4 layer1 = vec4(_BackgroundColor.rgb, 1.0);vec2 point1 = vec2(0.3, 0.8);vec2 point2 = vec2(0.8, 0.2);vec4 layer2 =  circle(pos, point1 * iResolution.xy, _Parameters.z, _CircleColor.rgb, _Parameters.w);vec4 layer3 =  circle(pos, point2 * iResolution.xy, _Parameters.z, _CircleColor.rgb, _Parameters.w);vec4 fragColor = mix(layer1, layer2, layer2.a);fragColor = mix(fragColor, layer3, layer3.a);return fragColor;}

In the code above, we drew two circles, one with the center of the circle (0.3, 0.8) and the other with the (0.8, 0.2. Layer1 is still the background layer, and layer2 and layer1 indicate the layers where the two circles are located. We call the lerp function in a hierarchical order (that is, the mix function in the Code) to mix these elements. The result is as follows:

 



This idea can be extended to any layer of element superposition ~



Draw a straight line


Now we know the two vertices on the straight line. Let's take a look at how to draw the straight lines of these two points. First, we can declare an empty function to draw a straight line, and add a new layer to it, and then fill in this function:

        vec4 line(vec2 pos, vec2 point1, vec2 point2, float width, float3 color, float antialias) {        return vec4(0);        }                vec4 circle(vec2 pos, vec2 center, float radius, float3 color, float antialias) {        float d = length(pos - center) - radius;        float t = smoothstep(0, antialias, d);        return vec4(color, 1.0 - t);        }        vec4 main(vec2 fragCoord) {vec2 pos = fragCoord; // pos.x ~ (0, iResolution.x), pos.y ~ (0, iResolution.y)vec2 point1 = vec2(0.3, 0.8) * iResolution.xy;vec2 point2 = vec2(0.8, 0.2) * iResolution.xy;vec4 layer1 = vec4(_BackgroundColor.rgb, 1.0);vec4 layer2 = line(pos, point1, point2, _LineWidth, _LineColor.rgb, _Antialias);vec4 layer3 =  circle(pos, point1, _CircleRadius, _CircleColor.rgb, _Antialias);vec4 layer4 =  circle(pos, point2, _CircleRadius, _CircleColor.rgb, _Antialias);vec4 fragColor = mix(layer1, layer2, layer2.a);fragColor = mix(fragColor, layer3, layer3.a);fragColor = mix(fragColor, layer4, layer4.a);return fragColor;}

For convenience, several new parameters are defined above. The complete code will be provided later, but I believe it is easy to understand here. I also adjusted the order of layers, that is, the second layer is a straight line, followed by a vertex layer, because we want the color of the vertex to overwrite the straight line color. After saving the image, the system returns the result without any changes, because the transparency of the color returned by the line layer is 0.


Note !!! Now it's time to show the charm of mathematics !!!The idea of drawing a straight line is actually very similar to that of a circle. We only need to determine whether the pixel position is in a straight line (because the straight line here has a width: determine whether the distance from the pixel to the straight line is less than half the width of the straight line. Then, we only need to recall the formula for distance from the point to the straight line in the current year. Have you forgotten it !!! The answer is provided here, and the formula is simple:

        vec4 line(vec2 pos, vec2 point1, vec2 point2, float width, float3 color, float antialias) {        float k = (point1.y - point2.y)/(point1.x - point2.x);    float b = point1.y - k * point1.x;        float d = abs(k * pos.x - pos.y + b) / sqrt(k * k + 1);    float t = smoothstep(width/2.0, width/2.0 + antialias, d);        return vec4(color, 1.0 - t);        }

We first obtain the parameters k and B (y = k * x + B) of the linear formula ). There is no critical judgment in the high school period, but the shader does not report an error ~ Then, we calculate the distance from the pixel to the straight line d. When calculating the transparency Factor t, we consider the width of the straight line, which means that when the distance d is less than width/2.0, 0 is returned, that is, the point is absolutely in the straight line; then, the anti-aliasing factor is used for the calculation.


It's easy !!! The effect is as follows:

 

 



The complete code is as follows:

Shader "shadertoy/Simple Line" {Properties {_ CircleRadius ("Circle Radius", float) = 5_CircleColor ("Circle Color", Color) = (1, 1, 1, 1) _ LineWidth ("Line Width", float) = 5_LineColor ("Line Color", Color) = (1, 1, 1, 1) _ Antialias ("Antialias Factor", float) = 3_BackgroundColor ("Background Color", Color) = (1, 1, 1, 1)} CGINCLUDE # include "UnityCG. cginc "# maid target 3.0 # define vec2 float2 # def Ine vec3 float3 # define vec4 float4 # define mat2 float2x2 # define iGlobalTime _ Time. y # define mod fmod # define mix lerp # define atan atan2 # define fract frac # define texture2D tex2D // screen size # define iResolution _ ScreenParams // coordinates on the screen, in pixel # define gl_FragCoord (_ iParam. srcPos. xy/_ iParam. srcPos. w) * _ ScreenParams. xy) # define PI2 6.28318530718 # define pi 3.14159265358979 # define halfpi (pi * 0.5) # Define oneoverpi (1.0/pi) float _ CircleRadius; float4 _ CircleColor; float _ LineWidth; float4 _ LineColor; float _ Antialias; float4 _ BackgroundColor; struct v2f {float4 pos: SV_POSITION; float4 srcPos: TEXCOORD0 ;}; // precision highp float; v2f vert (appdata_base v) {v2f o; o. pos = mul (UNITY_MATRIX_MVP, v. vertex); o. srcPos = ComputeScreenPos (o. pos); return o;} vec4 main (vec2 fragCoord); f Ixed4 frag (v2f _ iParam): COLOR0 {vec2 fragCoord = gl_FragCoord; return main (gl_FragCoord);} vec4 line (vec2 pos, vec2 point1, vec2 point2, float width, float3 color, float antialias) {float k = (point1.y-point2.y)/(point1.x-point2.x); float B = point1.y-k * point1.x; float d = abs (k * pos. x-pos. y + B)/sqrt (k * k + 1); float t = smoothstep (width/2.0, width/2.0 + antialias, d); return v Ec4 (color, 1.0-t);} vec4 circle (vec2 pos, vec2 center, float radius, float3 color, float antialias) {float d = length (pos-center) -radius; float t = smoothstep (0, antialias, d); return vec4 (color, 1.0-t);} vec4 main (vec2 fragCoord) {vec2 pos = fragCoord; // pos. x ~ (0, iResolution. x), pos. y ~ (0, iResolution. y) vec2 point1 = vec2 (0.4, 0.1) * iResolution. xy; vec2 point2 = vec2 (0.7, 0.8) * iResolution. xy; vec4 layer1 = vec4 (_ BackgroundColor. rgb, 1.0); vec4 layer2 = line (pos, point1, point2, _ LineWidth, _ LineColor. rgb, _ Antialias); vec4 layer3 = circle (pos, point1, _ CircleRadius, _ CircleColor. rgb, _ Antialias); vec4 layer4 = circle (pos, point2, _ CircleRadius, _ CircleColor. rgb, _ Antialias); vec4 fragColor = mix (layer1, layer2, layer2.a); fragColor = mix (fragColor, layer3, layer3.a); fragColor = mix (fragColor, layer4, layer4.a ); return fragColor;} ENDCG SubShader {Pass {CGPROGRAM # pragma vertex vert # pragma fragment frag # pragma fragmentoption ARB_precision_hint_fastest ENDCG} FallBack Off}



Conclusion


This article is very basic, but some knowledge is worth learning from real projects. Some people say that ShaderToy only contains toys and has no value. I personally don't think so ~ In the game, many delicate animation effects cannot be completed simply by textures. It is very helpful to know some basic or slightly complex shader computing ~


Finally, for the usefulness of ShaderToy, we can also improve it into a non-Pixel Shader version, for example, replacing screen coordinates with the uv coordinates of the model. More functions are available for you to discover!





Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.