WebGL - 着色器初始化流程

之前對着色器的初始化都是使用的initShaders()這個方法,部分代碼在文章最後,其內部分執行細節沒有去清晰的瞭解過,下面做進一步的瞭解和分析,才能更深入的瞭解webgl的原理

initShaders()方法的主要作用是編譯GLSL ES代碼,創建和初始化着色器,以便供WebgGL使用,具體初始化分爲以下七步;

  • 1、創建着色器對象gl.createShader()
  • 2、向着色器對象中填充着色器程序的源代碼gl.shaderSource()
  • 3、編譯着色器gl.compileShader()
  • 4、創建程序對象gl.createProgram()
  • 5、爲程序對象分配着色器gl.attachShader()
  • 6、連接程序對象gl.linkProgram()
  • 7、使用程序對象gl.useProgram()

1、兩個對象

着色器對象:着色器對象管理一個頂點着色器或者一個片元着色器

程序對象:程序對象是管理着着色器對象的容器

2、具體方法介紹

1、創建着色器對象 gl.createShader()

​ 通過gl.createShader()來創建着色器對象,gl.VERTEX_SHADER或者gl.FRAGMENT

gl.createShader(type)WebGL提供的一個方法,MDN文檔這樣解釋

WebGLRenderingContext.createShader() 用於創建一個 WebGLShader 着色器對象,該對象可以使用 WebGLRenderingContext.shaderSource()WebGLRenderingContext.compileShader() 方法配置着色器代碼.

該方法接收一個參數type

type

​ 參數爲gl.VERTEX_SHADERgl.FRAGMENT_SHADER兩者中的一個

如果不再需要這個着色器可以使用gl.deleteShader()將其刪除,但是如果着色器對象還在使用,那麼gl.deleteShader()並不會立刻將其刪除,而是要等到程序對象不再使用該着色器後,纔將其刪除

2、指定着色器對象的代碼gl.shaderSource()

gl.shaderSource(shader, source)方法,向着色器指定GLSL ES源代碼,在js中源代碼以字符串的形式存在;

3、編譯着色器gl.compileshader()

使用WebGLRenderingContext.compileShader()用於編譯一個GLSL着色器,使其成爲二進制數據,然後就可以被WebGLProgram對象所使用
語法

void gl.compileShader(shader)

var shader = gl.createShader(gl.VERTEX_SHADER);// 創建着色器對象
gl.shaderSource(shader, shaderSource);// 指定着色器對象的代碼
gl.compileShader(shader);// 編譯着色器

當使用gl.compileShader()方法時,如果着色器源代碼中存在錯誤,那麼就會出現編譯錯誤,可以使用gl.getShaderParameter()來檢查着色器的狀態

有以下參數

  • gl.DELETE_STATUS:標示着色器是否被刪除,刪除(GL_TRUE)未刪除(GL_FALSE).
  • gl.COMPILE_STATUS: 標示着色器是否編譯成功,是(GL_TRUE)不是(GL_FALSE
  • gl.SHADER_TYPE: 標示着色器類型,是頂點着色器(gl.VERTEX_SHADER)還是片段着色器(gl.FRAGMENT_SHADER)

返回true或者false

如果編譯失敗,返回false,此時webgl系統會把編譯錯誤的具體內容寫入着色器的 信息日誌,可以通過 gl.getShaderInfoLog()來獲取報錯信息

4、創建程序對象gl.createProgram()

程序對象包含頂點着色器和片元着色器,可以調用gl.createProfram()來創建程序對象,

該方法沒有參數,返回一個WebGLProgram對象

一個WebGLProgram對象,由兩個編譯過後的WebGLShader組成,頂點着色器和片元着色器,這些組合成一個可用的webgl着色器程序

示例:

var program = gl.createProgram();

// 添加預先定義好的頂點着色器和片段着色器
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

gl.linkProgram(program);

if ( !gl.getProgramParameter( program, gl.LINK_STATUS) ) {
  var info = gl.getProgramInfoLog(program);
  throw "Could not compile WebGL program. \n\n" + info;
}

類似的可以使用gl.deleteProgram()方法來刪除程序對象

一旦程序對象被創建之後,需要向程序附上兩個着色器

5、爲程序對象分配着色器對象gl.attachShader()

webgl要運行起來必須要有兩個着色器,頂點着色器和片元着色器,使用gl.attachShader()方法,爲程序對象分配這兩個着色器

如剛纔上面的那段代碼,就是在程序對象被創建之後開始分配着色器對象

着色器在附給程序對象前,並不一定要爲其指定代碼或進行編譯,也就是說把空的着色器附給程序對象也是可以的,也可以使用gl.detachShader()來解除分配給程序對象的着色器

6、連接程序對象gl.linkProgram()

在爲程序對象分配了兩個着色器對象後,還需要將着色器連接起來,使用gl.linkProgram()方法來執行

程序對象連接操作目的時保證以下幾點:

  • 頂點着色器和片元着色器的varying變量同名同類型,且一一對應
  • 頂點着色器對每個varying變量賦了值
  • 頂點着色器和片元着色器中的同名uniform變量也是同類型的
  • 着色器中的attributeuniform、和varying變量的個數沒有超過着色器的上限

如果程序已經連接成功了,那麼就會得到一個二進制的可執行模塊,供webgl系統使用

需要注意的是,程序對象即使連接成功了,也有可能運行失敗,比如沒有爲取樣器分配紋理單元,這些錯誤是在運行階段而不是連接階段產生的,在運行階段錯誤檢查的開銷很大,所以通常只在調式程序時這樣做;

7、使用程序對象gl.useProgram()

最後通過gl.useProgram()告知webgl系統使用哪個對象

WebGLRenderingContext.useProgram() 方法將定義好的WebGLProgram 對象添加到當前的渲染狀態中

這個方法使得webgl具有了一個強大的特性,那就是在繪製前準備多個程序對象,然後在繪製的時候根據需要切換程序對象

工具方法代碼

// cuon-utils.js (c) 2012 kanda and matsuda
/**
 * Create a program object and make current
 * @param gl GL context
 * @param vshader a vertex shader program (string)
 * @param fshader a fragment shader program (string)
 * @return true, if the program object was created and successfully made current
 */
function initShaders(gl, vshader, fshader) {
    var program = createProgram(gl, vshader, fshader);
    if (!program) {
        console.log('Failed to create program');
        return false;
    }
	
	// 使用程序對象
    gl.useProgram(program);
    // 將此時的程序對象夫hi給
    gl.program = program;

    return true;
}

/**
 * Create the linked program object
 * @param gl GL context
 * @param vshader a vertex shader program (string)
 * @param fshader a fragment shader program (string)
 * @return created program object, or null if the creation has failed
 */
function createProgram(gl, vshader, fshader) {
    // 創建着色器對象
    var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
    var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
    if (!vertexShader || !fragmentShader) {
        return null;
    }

    // 創建程序對象
    var program = gl.createProgram();
    if (!program) {
        return null;
    }

    // 爲程序對象分配着色器對象
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);

    // 連接程序對象
    gl.linkProgram(program);

    // Check the result of linking
    var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
        var error = gl.getProgramInfoLog(program);
        console.log('Failed to link program: ' + error);
        gl.deleteProgram(program);
        gl.deleteShader(fragmentShader);
        gl.deleteShader(vertexShader);
        return null;
    }
    return program;
}

/**
 * Create a shader object
 * @param gl GL context
 * @param type the type of the shader object to be created
 * @param source shader program (string)
 * @return created shader object, or null if the creation has failed.
 */
function loadShader(gl, type, source) {
    // 創建着色器對象
    var shader = gl.createShader(type);
    if (shader == null) {
        console.log('unable to create shader');
        return null;
    }

    // Set the shader program
    gl.shaderSource(shader, source);

    // Compile the shader
    gl.compileShader(shader);

    // Check the result of compilation
    var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
        var error = gl.getShaderInfoLog(shader);
        console.log('Failed to compile shader: ' + error);
        gl.deleteShader(shader);
        return null;
    }

    return shader;
}

/**
 * Initialize and get the rendering for WebGL
 * @param canvas <cavnas> element
 * @param opt_debug flag to initialize the context for debugging
 * @return the rendering context for WebGL
 */
function getWebGLContext(canvas, opt_debug) {
    // Get the rendering context for WebGL
    var gl = WebGLUtils.setupWebGL(canvas);
    if (!gl) return null;

    // if opt_debug is explicitly false, create the context for debugging
    if (arguments.length < 2 || opt_debug) {
        gl = WebGLDebugUtils.makeDebugContext(gl);
    }

    return gl;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章