JavaScript-WebGL2學習筆記三-紋理
阿新 • • 發佈:2018-11-19
這個例子還是由四個檔案組成
html原始檔清單
<html> <head> <!-- Date: 2018-3-19 Author: kagula Description: 相對於上一個例子 [1]去掉了透視投影變換. [2]添加了紋理顯示 [3]添加了列印投影后的紋理資料. Original: [1]《利用WebGL2 實現Web前端的GPU計算》 https://my.oschina.net/thesadabc/blog/1592866 測試環境 [1]Chrome 65.0.3325.162 [2]nginx 1.12.2 --> <title>我的第三個Webgl2演示</title> <meta charset="utf-8"> <!-- gl-matrix version 2.4.0 from http://glmatrix.net/ --> <script type="text/javascript" src="/gl-matrix-min.js"></script> <script type="text/javascript" src="/kagula/webgl2_helper.js"></script> </head> <body> <canvas id="glCanvas" width="320" height="200"></canvas> </body> </html> <script> main(); //弄4個頂點, 用來演示render流程! function initBuffers(gl) { // Create a buffer for the square's positions. const positionBuffer = gl.createBuffer(); // Select the positionBuffer as the one to apply buffer // operations to from here out. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // Now create an array of positions for the square. //WebGL最後會把計算好的影象資訊投影到左下角{-1,-1},右上角{1,1}的區域中。 const positions = [ 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, -1.0, ]; // Now pass the list of positions into WebGL to build the // shape. We do this by creating a Float32Array from the // JavaScript array, then use it to fill the current buffer. gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); return { position: positionBuffer }; } async function main() { //選擇器的使用 //http://www.runoob.com/jsref/met-document-queryselector.html const canvas = document.querySelector("#glCanvas"); // Initialize the GL context //為了獲取WebGL2上下文,getContext方法傳入的引數是"webgl2",而不是"webgl". const gl = canvas.getContext("webgl2"); // Only continue if WebGL is available and working if (!gl) { alert("Unable to initialize WebGL. Your browser or machine may not support it."); return; } //OpenGL ES 3.0 不支援多維陣列 //對傳入的陣列大小有限制 console.log("gl.MAX_VERTEX_UNIFORM_VECTORS=" + gl.MAX_VERTEX_UNIFORM_VECTORS + ", gl.MAX_FRAGMENT_UNIFORM_VECTORS=" + gl.MAX_FRAGMENT_UNIFORM_VECTORS); //裝配shader到shaderProgram中去 const vsSource = await loadResource("../shader/simple.vs"); const fsSource = await loadResource("../shader/simple.fs"); const shaderProgram = initShaderProgram(gl, vsSource, fsSource); //為了讓外部的資料能統一傳到shanderProgram中去,新建programInfo物件。 //vertexPosition => aVertexPosition位置 //projectionMatrix => uProjectionMatrix位置 //modelViewMatrix => uModelViewMatrix位置 //... const programInfo = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition') }, uniformLocations: { samplerA: gl.getUniformLocation(shaderProgram, 'samplerA') }, }; //initBuffers(gl)返回要render的vertex. drawScene(gl, programInfo, initBuffers(gl)); }//main </script>
webgl2_helper.js
async function loadResource(remoteFile) { try { let response = await fetch(remoteFile); return response.text(); console.log(data); } catch(e) { console.log("Oops, error", e); } } // Initialize a shader program, so WebGL knows how to draw our data function initShaderProgram(gl, vsSource, fsSource) { const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); // Create the shader program const shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); // If creating the shader program failed, alert if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); return null; } return shaderProgram; } // creates a shader of the given type, uploads the source and // compiles it. function loadShader(gl, type, source) { const shader = gl.createShader(type); // Send the source to the shader object gl.shaderSource(shader, source); // Compile the shader program gl.compileShader(shader); // See if it compiled successfully if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); gl.deleteShader(shader); return null; } return shader; } function drawScene(gl, programInfo, buffers) { gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque gl.clearDepth(1.0); // Clear everything gl.enable(gl.DEPTH_TEST); // Enable depth testing gl.depthFunc(gl.LEQUAL); // Near things obscure far things // Clear the canvas before we start drawing on it. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); //這裡準備vertex shader需要的資料 //整個pipeline可以看成下面的流程 //我們使用WebGL準備資料 => Vertex Shader => WebGL => Fragment Shader => WebGL => Canvas // Tell WebGL how to pull out the positions from the position // buffer into the vertexPosition attribute. { // gl.ARRAY_BUFFER => 指向 => buffers.position gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); //指定源資料格式. //https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer gl.vertexAttribPointer( programInfo.attribLocations.vertexPosition, 2,// pull out 2 values per iteration //Must be 1, 2, 3, or 4. 比如說頂點{x,y}要選2,{x,y,z}要選3,顏色{r,g,b,a}要選4 gl.FLOAT,// the data in the buffer is 32bit floats false,// don't normalize 0,//stride, how many bytes to get from one set of values to the next 0);//how many bytes inside the buffer to start from //源資料填充到gl //tell WebGL that this attribute should be filled with data from our array buffer. //gl.ARRAY_BUFFER => 資料傳到 => programInfo.attribLocations.vertexPosition gl.enableVertexAttribArray( programInfo.attribLocations.vertexPosition); } // Tell WebGL to use our program when drawing gl.useProgram(programInfo.program); //準備fragment shader要用到的紋理資料,並把它傳入到webGL中。 { function initTexture(index, pixels) { let dim = 3;//3*3 size 大小紋理。 const texture = gl.createTexture(); gl.activeTexture(gl[`TEXTURE${index}`]); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);//如果沒有這行程式碼,WebGL會採用“插入過渡顏色”來進行紋理對映。 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, dim, dim, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels, 0); gl.uniform1i(programInfo.uniformLocations.samplerA, index); } const colorMap = new Uint32Array([ 0xFF0000FF, 0x00FF00FF, 0x0000FFFF, 0xFFFF00FF, 0xFF00FFFF, 0x00FFFFFF, 0x000000FF, 0xFFFFFFFF, 0xF0F0F0FF, ]); const RGBAMap = new Uint8Array(colorMap.buffer); initTexture(0, RGBAMap); } //資料準備好後可以draw了. { const offset = 0; const vertexCount = 4; //開始處理已經在gl中的頂點資料 //gl.TRIANGLE_STRIP模式復用前面兩個頂點, 所以這裡告訴gl, render兩個三角形. gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); } { const canvas = document.querySelector("#glCanvas"); let picBuf = new ArrayBuffer(canvas.width * canvas.height * 4); let picU8 = new Uint8Array(picBuf); let picU32 = new Uint32Array(picBuf); //void gl.readPixels(x, y, width, height, format, type, ArrayBufferView pixels, GLuint dstOffset); //x A GLint specifying the first horizontal pixel that is read from the lower left corner of a rectangular block of pixels. //y A GLint specifying the first vertical pixel that is read from the lower left corner of a rectangular block of pixels. //width A GLsizei specifying the width of the rectangle. //height A GLsizei specifying the height of the rectangle. //format A GLenum specifying the format of the pixel data. Possible values: // gl.ALPHA: Discards the red, green and blue components and reads the alpha component. // gl.RGB: Discards the alpha components and reads the red, green and blue components. // gl.RGBA: Red, green, blue and alpha components are read from the color buffer. //type A GLenum specifying the data type of the pixel data. Possible values: // gl.UNSIGNED_BYTE // gl.UNSIGNED_SHORT_5_6_5 // gl.UNSIGNED_SHORT_4_4_4_4 // gl.UNSIGNED_SHORT_5_5_5_1 // gl.FLOAT //pixels An ArrayBufferView object to read data into. The array type must match the type of the type parameter. // Uint8Array for gl.UNSIGNED_BYTE. // Uint16Array for gl.UNSIGNED_SHORT_5_6_5, gl.UNSIGNED_SHORT_4_4_4_4, or gl.UNSIGNED_SHORT_5_5_5_1. // Float32Array for gl.FLOAT. //具體參考 //https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/readPixels gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, picU8); console.log(picU32); } }
simple.vs
#version 300 es //指定float int的精度 precision highp float; precision highp int; //從裝置外部傳入的資料. in vec4 aVertexPosition; out vec2 frag_vpos; void main() { //從{左下角,右上角}={(-1,-1),(1,1)}轉到{左下角,右上角}={(0,0),(1,1)} frag_vpos.x = (aVertexPosition.x + 1.0)/2.0; frag_vpos.y = (aVertexPosition.y + 1.0)/2.0; //vertex shader輸出的座標是以canvas中心為(0,0) 水平向右為x軸正方向 垂直向上為y軸正方向 兩軸的取值範圍為[-1, 1] gl_Position = aVertexPosition; }
simple.fs
#version 300 es
precision highp float;
precision highp int;
in vec2 frag_vpos;
uniform sampler2D samplerA;
//output to pipeline
out vec4 myOutputColor;
void main() {
vec4 color = texture(samplerA, frag_vpos);
myOutputColor = color.abgr;
}