Shader

WebGL並未提供一個固定渲染管道(Fixed Function Pipeline)。而他所提供的是可編程管道(Programable Function Pipeline),更為強大,卻也複雜。可編程管道的意思是程式設計師需要先找到這些點才能夠渲染至螢幕上。Shaders就是這種管道的一部分,在這裡我們有兩種個以選擇:

  1. Vertex shaders
  2. Fragment shaders

我想你會同意這兩者的名稱並沒什麼特殊的意義,你僅需要知道它們只在你的GPU上運行。高級的GPU會盡可能優化shader所使用的到的函數,讓我們能夠更好的使用。

關於Fixed function pipeline與Programable Function Pipeline參考:

https:\/\/cg2010studio.com\/2011\/06\/29\/shader\/

https://www.ptt.cc/bbs/GameDesign/M.1425531225.A.23D.html

VERTEX SHADERS

舉例來說,一個球體為不同的vertex所構成,而會有個vertex shader會依序的給予到每個vertex並且能夠更動他們。

無論vertex shader做了什麼,他有個必要的任務:在某個點上設置 gl_Position ,一個四維的浮點數向量,它是vertex最後在螢幕上的位置。這是個很有序的過程,畢竟我們是在討論3維的位置或是2維的螢幕。幸好,在我們使用Three.js之類的套件時,我們有更簡單的方式去設定gl_Position。

FRAGMENT SHADERS

在我們有了這些vertices構成的物件,且將他們投影到我們的2D螢幕上之時,那我們的顏色呢?或是紋理及光影?這些就是fragment shader在做的事情。

有點像vertex shader在做的事情一般,fragment shader也有個必須做到的任務:他需要設定或消除 gl_FragColor 變數,同樣為一個四維浮點數向量,為fragment最終的顏色。那麼,什麼是fragment呢?讓我們考慮三個vertices所構成的一個三角形吧,在這三角形上所有的pixel都需要被繪製出。fragment是由那三個vertices所提供之繪製三角形上所有pixel的資料。

Because of this the fragments receive interpolated values from their constituent vertices. If one vertex is coloured red, and its neighbour is blue we would see the colour values interpolate from red, through purple, to blue.

SHADER VARIABLES

在我們討論這些變數前,你三種可以宣告:Uniforms、Attributes、Varyings。 你可以這樣想:

Uniforms are sent to both vertex shaders and fragment shaders and contain values that stay the same across the entire frame being rendered. A good example of this might be a light’s position.

Uniforms是發送給vertex shaders及fragment shaders,且在render時他的值在整個frame之中都相同。一例為light的位置。

Attributes are values that are applied to individual vertices. Attributes are only available to the vertex shader. This could be something like each vertex having a distinct colour. Attributes have a one-to-one relationship with vertices.

Attributes只使用在獨立的vertices上,且只有vertex shader會用到。這可能是在不同點個別有不同顏色時會用到,也就是說Attributes與點有一對一的關係。

Varyings are variables declared in the vertex shader that we want to share with the fragment shader. To do this we make sure we declare a varying variable of the same type and name in both the vertex shader and the fragment shader. A classic use of this would be a vertex’s normal since this can be used in the lighting calculations.

Varyings 是我們在vertex shadder中宣告與fragment shader分享的變數。在做這個時,我們要確定我們在vertex shader和fragment shader上以相同型別及名稱宣告varying variable。在一般的使用上這可能是一個vertex的normal,讓他可以在lighting的計算上使用。

現在我們已經說明了這兩種shaders與能夠使用的變數型別了,現在就讓我們來看看shader最簡單的例子:

Here, then, is the Hello World of vertex shaders:

/**
 * Multiply each vertex by the
 * model-view matrix and the
 * projection matrix (both provided
 * by Three.js) to get a final
 * vertex position
 */
void main() {
  gl_Position = projectionMatrix *modelViewMatrix *vec4(position,1.0);
}

and here’s the same for the fragment shader:

/**
 * Set the colour to a lovely pink.
 * Note that the color is a 4D Float
 * Vector, R,G,B and A and each part
 * runs from 0.0 to 1.0
 */
void main() {
  gl_FragColor = vec4(1.0,  // R
                      0.0,  // G
                      1.0,  // B
                      1.0); // A
}

That’s really all there is to it. If you were to use that you would see an ‘unlit’ pink shape on your screen. Not too complicated though, right?

In the vertex shader we are sent a couple of uniforms by Three.js. These two uniforms are 4D matrices, called the Model-View Matrix and the Projection Matrix. You don’t desperately need to know exactly how these work, although it’s always best to understand how things do what they do if you can. The short version is that they are how the 3D position of the vertex is actually projected to the final 2D position on the screen.

I’ve actually left them out of the snippet above because Three.js adds them to the top of your shader code itself so you don’t need to worry about doing it. Truth be told it actually adds a lot more than that, such as light data, vertex colours and vertex normals. If you were doing this without Three.js you would have to create and set all those uniforms and attributes yourself. True story.

USING A MESHSHADERMATERIAL

OK, so we have a shader set up, but how do we use it with Three.js? It turns out that it’s terribly easy. It’s rather like this:

/**
 * Assume we have jQuery to hand
 * and pull out from the DOM the
 * two snippets of text for
 * each of our shaders
 */
var vShader = $('vertexshader');
var fShader = $('fragmentshader');
var shaderMaterial =
  new THREE.ShaderMaterial({
    vertexShader:   vShader.text(),
    fragmentShader: fShader.text()
  });

From there Three.js will compile and run your shaders attached to the mesh to which you give that material. It doesn’t get much easier than that really. Well it probably does, but we’re talking about 3D running in your browser so I figure you expect a certain amount of complexity.

We can actually add two more properties to our MeshShaderMaterial: uniforms and attributes. They can both take vectors, integers or floats but as I mentioned before uniforms are the same for the whole frame, i.e. for all vertices, so they tend to be single values. Attributes, however, are per- vertex variables, so they are expected to be an array. There should be a one- to-one relationship between the number of values in the attributes array and the number of vertices in the mesh.

CONCLUSION I’ll stop there for now as we’ve actually covered a rather large amount, and yet in many ways we’ve only just scratched the surface. In the next guide I’m going provide a more advanced shader to which I will be passing through some attributes and uniforms as well as doing a bit of fake lighting.

I’ve wrapped up the source code in this lab article so you have it as a reference. If you’ve enjoyed this let me know via Twitter - it makes for a happy Paul.

資料來源:

https:\/\/aerotwist.com\/tutorials\/an-introduction-to-shaders-part-1\/

results matching ""

    No results matching ""