|
| 1 | +# Tutorial 1 |
| 2 | + |
| 3 | +In this tutorial we explore the most basic fullscreen pass: The final pass. |
| 4 | + |
| 5 | +## Basic file structure of a shader |
| 6 | + |
| 7 | +Within a shader pack, there is a folder called "shaders". This is where all the shader code goes. All shader files have the extension `.vsh` for vertex shaders or `.fsh` for fragment shaders. The shaders with the same file name will become part of the same program. For example, in this case, `final.vsh` and `final.fsh` get linked toghter to form the `final` program. |
| 8 | + |
| 9 | +## What is final? |
| 10 | + |
| 11 | +final is a fullscreen pass. It is the last pass in the shader pipeline. Whatever color final outputs is the color that gets displayed on your screen. Here, you can do certain post processing effects like bluring, tone mapping, or gamma correction. |
| 12 | + |
| 13 | +## What other fullscreen passes are there? |
| 14 | + |
| 15 | +In Optifine, there are 3 types of fullscreen passes. They are called `deferred`, `composite`, and `final`. `final` is the fullscreen pass we just covered. `deferred` and `compsite` are fullscreen passes we will cover in later tutorials. |
| 16 | + |
| 17 | +## Show me the code |
| 18 | + |
| 19 | +We will be implementing a basic shader that converts the colors on your screen to grayscale. Let's start off with the vertex shader. We first start with the version declratation. We will be using GLSL version 120 |
| 20 | + |
| 21 | +```glsl |
| 22 | +#version 120 |
| 23 | +``` |
| 24 | + |
| 25 | +Since final is a fullscreen pass, we need to pass a texture coordinate into the fragment shader. |
| 26 | + |
| 27 | +```glsl |
| 28 | +varying vec2 TexCoords; |
| 29 | +``` |
| 30 | + |
| 31 | +Now we head into the `main` function of the vertex shader. |
| 32 | + |
| 33 | +```glsl |
| 34 | +void main() { |
| 35 | + gl_Position = ftransform(); |
| 36 | + TexCoords = gl_MultiTexCoord0.st; |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +If you have never seen `ftransform` or `gl_MultiTexCoord0` before, don't worry. They are part of the old versions of the GLSL shading language. `ftransform` basically expands to `gl_ModelViewProjectionMatrix * gl_Vertex`. `gl_Vertex` is the in-built vertex attribute.`gl_ModelViewProjectionMatrix` is the in-build model view projection matrix. Since `gl_Vertex` is probably in clip space already, `gl_ModelViewProjectionMatrix` is the identity matrix. `gl_MultiTexCoord0` is the in-built texture coordinate attribute. If you are wondering, yes, there is `gl_MultiTexCoord1`, `gl_MultiTexCoord2`, etc. We will look into using `gl_MultiTexCoord1` later. `gl_MultiTexCoord2` and higher are usually not used. In built texture coordinates are `vec4`, which is why we have to add `.st` at the end. |
| 41 | + |
| 42 | +End the end your vertex shader looks like this: |
| 43 | + |
| 44 | +```glsl |
| 45 | +#version 120 |
| 46 | +
|
| 47 | +varying vec2 TexCoords; |
| 48 | +
|
| 49 | +void main() { |
| 50 | + gl_Position = ftransform(); |
| 51 | + TexCoords = gl_MultiTexCoord0.st; |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +After the vertex shader comes the fragment shader. We start off by declaring the GLSL version and accepting the texture coordinate output from the vertex shader. |
| 56 | + |
| 57 | +```glsl |
| 58 | +#version 120 |
| 59 | +
|
| 60 | +varying vec2 TexCoords; |
| 61 | +``` |
| 62 | + |
| 63 | +Since final is a fullscreen pass, we need to sample the screen's color from somewhere. Since we have not defined any other program besides final, Optifine will use it's internal shader for the missing programs. The internal shader is basically a reimplementation of the vanilla shaders in the shader pipeline. The internal shaders outputs it's color to a texture called `colortex0`. |
| 64 | + |
| 65 | +```glsl |
| 66 | +uniform sampler2D colortex0; |
| 67 | +``` |
| 68 | + |
| 69 | +Now we enter the `main` function: |
| 70 | + |
| 71 | +```glsl |
| 72 | +void main() { |
| 73 | + // Sample the color |
| 74 | + vec3 Color = texture2D(colortex0, TexCoords).rgb; |
| 75 | + // Convert to grayscale |
| 76 | + Color = vec3(dot(Color, vec3(0.333f))); |
| 77 | + // Output the color |
| 78 | + gl_FragColor = vec4(Color, 1.0f); |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +Let's break it down line by line. We first sample `colortex0` using our texture coordinate. We use the function `texture2D` here since that is how you sampled from a 2D texture in old versions of GLSL. More modern versions have replaced this function with `texture`, however, that is not available in GLSL 120. We then convert the color to grayscale using `dot(Color, vec3(0.333f))` which is mathematically equivalent to `Color.r * 0.333f + Color.g * 0.333f + Color.b * 0.333f`. Then we finally output the color to `gl_FragColor`. |
| 83 | + |
| 84 | +In the end, your fragment shader should be this: |
| 85 | + |
| 86 | +```glsl |
| 87 | +#version 120 |
| 88 | +
|
| 89 | +varying vec2 TexCoords; |
| 90 | +
|
| 91 | +uniform sampler2D gcolor; |
| 92 | +
|
| 93 | +void main() { |
| 94 | + vec3 Color = texture2D(gcolor, TexCoords).rgb; |
| 95 | + Color = vec3(dot(Color, vec3(0.333f))); |
| 96 | + gl_FragColor = vec4(Color, 1.0f); |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +Here is the results of the shader: |
| 101 | + |
| 102 | + |
| 103 | + |
| 104 | +Although it is not much, it's definitely a start on your shader programming journey! |
0 commit comments