Skip to main content

attribute Variables and Vertex Shader_WebGL Notes 2

Free2015-12-22#JS#WebGL#webgl attribute变量#js向webgl传值#顶点着色器的attribute变量#顶点着色器#片元着色器#片元

By passing values to vertex shader through attribute variables, we can dynamically modify point positions

Preface

In [previous notes](/articles/用 webgl 绘制一个矩形-webgl 笔记 1/) we hardcoded gl_Position in vertex shader source program, to modify point position we'd have to modify shader source program, this of course has no meaning, we need a flexible enough way, such as attribute variables

1. Encapsulating util

In [previous notes](/articles/用 webgl 绘制一个矩形-webgl 笔记 1/) to draw a fake rectangle (a slightly larger point), we wrote over 70 lines of code, and only included error checking, compatibility handling wasn't done yet. Every step was calling WebGL native API, although flow is clear, but really too troublesome, 1 point 70 lines, drawing two points 140 lines won't work, so, first start encapsulating util

Before starting, need to note several issues:

1.getContext Compatibility Issue

getContext('webgl') is standard way, but in low version browsers need to pass different strings, as follows:

/**
 * Creates a webgl context.
 * @param {!Canvas} canvas The canvas tag to get context
 *     from. If one is not passed in one will be created.
 * @return {!WebGLContext} The created context.
 */
var create3DContext = function(canvas, opt_attribs) {
  var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
  var context = null;
  for (var ii = 0; ii < names.length; ++ii) {
    try {
      context = canvas.getContext(names[ii], opt_attribs);
    } catch(e) {}
    if (context) {
      break;
    }
  }
  return context;
}

Above code excerpted from google team's webgl-utils.js, besides create3DContext also provides compatible version of requestAnimationFrame, will also be used for animation later, so our util directly includes webgl-utils.js

2.Keep Reference to program Object

gl.createProgram() returned program object not only used in shader initialization process, but also will be used in attribute variable value passing we're about to do, program object is bridge for js and shader internal communication, but problem is WebGL doesn't provide getter like gl.getProgram(), so when encapsulating util must keep reference to program object, for external use, for example:

function initShaders(vsSrc, fsSrc) {
    var program = _createProgram(vsSrc, fsSrc);
    
    if (!program) {
        log('Failed to create program');
        return false;
    }
    gl.useProgram(program);

    // Save program object
    //!!! because there is no getter like gl.getProgram()
    glUtil.program = program;

    return true;
}

Above code excerpted from author's gl-util.js, can directly use if needed, interface description as follows:

// Global variable: glUtil
return {
    program: null,  // runtime assignment

    getContext: getContext,
    initShaders: initShaders,

    debug: function(onOrOff) {
        if (typeof onOrOff === 'boolean') {
            debug = onOrOff;
        }
    }
};

2. Terminology and API Explanation

1.Shader

Shaders are divided into vertex shader and fragment shader:

  • Vertex Shader

Program used to describe vertex characteristics (such as position, color, etc.). Vertex refers to a point in two-dimensional or three-dimensional space, such as endpoints or intersections of two-dimensional or three-dimensional graphics

  • Fragment Shader

Program that performs per-fragment processing (such as lighting). Fragment is a WebGL term, can simply understand as pixel (unit of image)

P.S. Actually fragment represents a pixel displayed on screen, also includes this pixel's position, color and other information

2.Vertex Shader Source Program

Program written in GLSL ES, passed into WebGL system internally as string

// 顶点着色器源程序
var vsSrc = 'void main() {' +
    'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 设置坐标
    'gl_PointSize = 200.0;' +                   // 设置尺寸
'}';

Among them, assigning to gl_Position is necessary, otherwise shader cannot work normally, gl_Position is vec4 type, i.e. four-dimensional vector (homogeneous coordinates, last component generally takes 1.0)

gl_PointSize has default value 1.0, can skip assigning, gl_PointSize is float type, doesn't accept form like 3f (3f in C language represents float 3), and only takes effect when drawing isolated points (what is drawing isolated points, we'll say later)

3.Fragment Shader Source Program

Same as vertex shader source program, but controls fragment shader

// 片元着色器源程序
var fsSrc = 'void main() {' +
    'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 设置颜色
'}';

gl_FragColor is only built-in variable, also vec4 type, but represents rgba color value, rgba values are all 0~1, different from CSS where rgb is 0~255

4.Drawing Operation

Drawing operation has only one interface, but functionality is very powerful:

// 绘制矩形(一个点,但点的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);

API specific information as follows:

gl.drawArrays(mode, first, count)

mode specifies drawing method, accepts constants:
---
gl.POINTS           Isolated points (v0, v1...)
gl.LINES            Isolated line segments (v0v1, v2v3...)
gl.LINE_STRIP       Continuous line segments (v0v1, v1v2...)
gl.LINE_LOOP        Continuous line segments forming loop (v0v1, v1v2...vnv0)
gl.TRIANGLES        Isolated triangles (v0v1v2, v3v4v5...)
gl.TRIANGLE_STRIP   Continuous triangle strip (v0v1v2, v2v1v3, v2v3v4...), used to construct complex models, such as sphere, tree
                    Note: second triangle is v2v1v3, not v1v2v3, this is to ensure second triangle's drawing also follows counterclockwise order
gl.TRIANGLE_FAN     Triangle fan (v0v1v2, v0v2v3, v0v3v4), can also be used to construct complex models, but not common in practical applications, because need to find all triangles' common vertex v0

first specifies which vertex to start drawing from
---
0 means start from first, used with buffer object (buffer object can store multiple vertex information)

count specifies number of points to draw, also used with buffer object
---
When calling gl.drawArray, vertex shader will be executed count times, each time processing one vertex, after vertex shader execution completes fragment shader starts executing, coloring fragments (also has rasterization process in between)

Special note: Vertex shader executes per vertex, fragment shader executes per fragment, not just executes once

3. attribute Variables

Declare a variable in shader source program, then js passes value to this variable, then draw, achieves dynamically modifying point position, simply put that's it

1.Declaring attribute Variable

// 顶点着色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
    'void main() {' +
    'gl_Position = a_Position;' +   // 设置坐标
    'gl_PointSize = 200.0;' +       // 设置尺寸
'}';
// 片元着色器源程序
//...不变

Similar to C language variable declaration way, attribute is called storage qualifier, vec4 is variable type. Variable name a_Position where a_ prefix indicates variable is attribute variable, of course this is just habit, suggest adopting

P.S. Why declare attribute variable in vertex shader source program, not in fragment... because we want to dynamically modify point coordinates, coordinate information comes from gl_Position in vertex shader source program, has nothing to do with fragment shader

Note: Only vertex shader can use attribute variables, fragment shader needs to use uniform variables, additionally there are varying variables, specific differences will be explained in detail in GLSL ES notes

2.Initialize Shader

Directly call util, 1 line of code can complete remaining 7 things (7 steps from createShader to useProgram)

// 1.初始化着色器
glUtil.initShaders(vsSrc, fsSrc);

3.Assign Value to attribute Variable

Variable assignment is divided into 2 steps, first get storage location, then assign value

// 2.给 attribute 变量赋值
// 获取 attribute 变量的存储位置
var a_Position = gl.getAttribLocation(glUtil.program, 'a_Position');
if (a_Position < 0) {
    console.log('Failed to get the storage location of a_Position');
    return;
}
// 把顶点位置传递给 attribute 变量
gl.vertexAttrib3f(a_Position, 0.5, 0.0, 0.0);

4.Draw

// 绘制点
gl.drawArrays(gl.POINTS, 0, 1);

First parameter passes gl.POINTS indicates drawing isolated points, can also draw line segments, triangles etc., will discuss later

4. DEMO

Complete example containing above code, please check: http://www.ayqy.net/temp/webgl/attribute/index.html

5. Summary

Now we can dynamically modify vertex coordinates, of course, this is just a very simple step,后面还有:

  • How to draw a point at mouse click position?

Answer is not as simple as imagined, canvas and WebGL coordinate systems are different, need to convert coordinates

  • How to modify point color?

Use uniform variables is enough

  • How to draw multiple points

Loop gl.drawArrays(gl.POINTS, 0, 1);? Is it okay?

These are all problems to discuss in later notes, don't rush first

Reference Materials

  • "WebGL Programming Guide"

Comments

No comments yet. Be the first to share your thoughts.

Leave a comment