你如何在WebGL上绘制一个平面贴图?

时间:2017-11-10 03:37:34

标签: javascript webgl

我创建了一个由地板(平面),桌子和立方体组成的基本场景。我现在想要使用木质纹理图像Click to see the wooden texture.在地板上进行纹理处理。我对纹理很少了解,而且对WebGL很新。

我有一些想法,必须从我的目录加载图像,但不知道如何将其应用到地板。除此之外,我不确定平面(地板)所需的纹理顶点坐标。

我也知道我需要添加新属性以允许纹理化。您是否需要更改对象的整体布局以进行纹理?

对于从哪里开始以及如何开展这项任务表示感谢。

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <title>Drawing In 3D </title>
        <meta charset="utf-8">
        <script src="glMatrix.js"></script>
        <script src="webgl-debug.js"></script>
        <script id="shader-vs" type="x-shader/x-vertex">
            attribute vec3 aVertexPosition;
            attribute vec4 aVertexColor;
            uniform mat4 uMVMatrix;
            uniform mat4 uPMatrix;
            varying vec4 vColor;
            void main() {
                gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
                vColor = aVertexColor;
            }
        </script>
    
        <script id="shader-fs" type="x-shader/x-fragment">
          precision mediump float;
          varying vec4 vColor;
          void main() {
                            gl_FragColor = vColor;
                        }
        </script>
        <script type="text/javascript">
            var gl;
            var canvas;
            var shaderProgram;
            var floorVertexPositionBuffer;
            var floorVertexIndexBuffer;
            var cubeVertexPositionBuffer;
            var cubeVertexIndexBuffer;
            var modelViewMatrix;
            var projectionMatrix;
            var modelViewMatrixStack;
    
            function createGLContext(canvas) {
                var names = ["webgl", "experimental-webgl"];
                var context = null;
                for (var i = 0; i < names.length; i++) {
                    try {
                        context = canvas.getContext(names[i]);
                    } catch (e) { }
                    if (context) {
                        break;
                    }
                }
                if (context) {
                    context.viewportWidth = canvas.width;
                    context.viewportHeight = canvas.height;
                } else {
                    alert("Failed to create WebGL context!");
                }
                return context;
            }
    
            function loadShaderFromDOM(id) {
                var shaderScript = document.getElementById(id);
                if (!shaderScript) {
                    return null;
                }
                var shaderSource = "";
                var currentChild = shaderScript.firstChild;
                while (currentChild) {
                    if (currentChild.nodeType == 3) { // 3 corresponds to TEXT_NODE
                        shaderSource += currentChild.textContent;
                    }
                    currentChild = currentChild.nextSibling;
                }
    
                var shader;
                if (shaderScript.type == "x-shader/x-fragment") {
                    shader = gl.createShader(gl.FRAGMENT_SHADER);
                } else if (shaderScript.type == "x-shader/x-vertex") {
                    shader = gl.createShader(gl.VERTEX_SHADER);
                } else {
                    return null;
                }
    
                gl.shaderSource(shader, shaderSource);
                gl.compileShader(shader);
    
                if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                    alert(gl.getShaderInfoLog(shader));
                    return null;
                }
                return shader;
            }
    
            function setupShaders() {
                var vertexShader = loadShaderFromDOM("shader-vs");
                var fragmentShader = loadShaderFromDOM("shader-fs");
                shaderProgram = gl.createProgram();
                gl.attachShader(shaderProgram, vertexShader);
                gl.attachShader(shaderProgram, fragmentShader);
                gl.linkProgram(shaderProgram);
    
                if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
                    alert("Failed to setup shaders");
                }
    
                gl.useProgram(shaderProgram);
                shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
                shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
                shaderProgram.uniformMVMatrix = gl.getUniformLocation(shaderProgram, "uMVMatrix");
                shaderProgram.uniformProjMatrix = gl.getUniformLocation(shaderProgram, "uPMatrix");
    
                // Initialise the matrices
                modelViewMatrix = mat4.create();
                projectionMatrix = mat4.create();
                modelViewMatrixStack = [];
                gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
            }
    
            function pushModelViewMatrix() {
                var copyToPush = mat4.create(modelViewMatrix);
                modelViewMatrixStack.push(copyToPush);
            }
    
            function popModelViewMatrix() {
                if (modelViewMatrixStack.length == 0) {
                    throw "Error popModelViewMatrix() - Stack was empty ";
                }
                modelViewMatrix = modelViewMatrixStack.pop();
            }
    
            function setupFloorBuffers() {
                floorVertexPositionBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexPositionBuffer);
    
                var floorVertexPosition = [
                    // Plane in y=0
                    5.0, 0.0, 5.0,  //v0
                    5.0, 0.0, -5.0,  //v1
                    -5.0, 0.0, -5.0,  //v2
                    -5.0, 0.0, 5.0]; //v3
    
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(floorVertexPosition), gl.STATIC_DRAW);
                floorVertexPositionBuffer.itemSize = 3;
                floorVertexPositionBuffer.numberOfItems = 4;
                floorVertexIndexBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, floorVertexIndexBuffer);
                var floorVertexIndices = [0, 1, 2, 3];
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(floorVertexIndices), gl.STATIC_DRAW);
                floorVertexIndexBuffer.itemSize = 1;
                floorVertexIndexBuffer.numberOfItems = 4;
            }
    
            function setupCubeBuffers() {
                cubeVertexPositionBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
    
                var cubeVertexPosition = [
                    1.0, 1.0, 1.0, //v0
                    -1.0, 1.0, 1.0, //v1
                    -1.0, -1.0, 1.0, //v2
                    1.0, -1.0, 1.0, //v3
                    1.0, 1.0, -1.0, //v4
                    -1.0, 1.0, -1.0, //v5
                    -1.0, -1.0, -1.0, //v6
                    1.0, -1.0, -1.0, //v7
                ];
    
                gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(cubeVertexPosition), gl.STATIC_DRAW);
                cubeVertexPositionBuffer.itemSize = 3;
                cubeVertexPositionBuffer.numberOfItems = 8;
                cubeVertexIndexBuffer = gl.createBuffer();
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
    
                var cubeVertexIndices = [
                    0, 1, 2, 0, 2, 3,    // Front face
                    4, 6, 5, 4, 7, 6,    // Back face
                    1, 5, 6, 1, 6, 2,	//left
                    0, 3, 7, 0, 7, 4,	//right
                    0, 5, 1, 0, 4, 5,	//top
                    3, 2, 6, 3, 6, 7 	//bottom
                ];
    
                gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
                cubeVertexIndexBuffer.itemSize = 1;
                cubeVertexIndexBuffer.numberOfItems = 36;
            }
    
            function setupBuffers() {
                setupFloorBuffers();
                setupCubeBuffers();
            }
    
            function uploadModelViewMatrixToShader() {
                gl.uniformMatrix4fv(shaderProgram.uniformMVMatrix, false, modelViewMatrix);
            }
    
            function uploadProjectionMatrixToShader() {
                gl.uniformMatrix4fv(shaderProgram.uniformProjMatrix, false, projectionMatrix);
            }
    
            function drawFloor(r, g, b, a) {
    
                // Disable vertex attrib array and use constant color for the floor.
                gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
    
                // Set colour
                gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, r, g, b, a);
    
                // Draw the floor
                gl.bindBuffer(gl.ARRAY_BUFFER, floorVertexPositionBuffer);
                gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, floorVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, floorVertexIndexBuffer);
                gl.drawElements(gl.TRIANGLE_FAN, floorVertexIndexBuffer.numberOfItems, gl.UNSIGNED_SHORT, 0);
            }
    
            function drawCube(r, g, b, a) {
    
                // Disable vertex attrib array and use constant color for the cube.
                gl.disableVertexAttribArray(shaderProgram.vertexColorAttribute);
    
                // Set color
                gl.vertexAttrib4f(shaderProgram.vertexColorAttribute, r, g, b, a);
                gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
                gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
                gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
                gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numberOfItems, gl.UNSIGNED_SHORT, 0);
            }
    
            function drawTable() {
    
                // Draw table top
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 1.0, 0.0], modelViewMatrix);
                mat4.scale(modelViewMatrix, [2.0, 0.1, 2.0], modelViewMatrix);
                uploadModelViewMatrixToShader();
    
                // Draw the scaled cube
                drawCube(0.72, 0.53, 0.04, 1.0); // brown color
                popModelViewMatrix();
    
                // Draw table legs
                for (var i = -1; i <= 1; i += 2) {
                    for (var j = -1; j <= 1; j += 2) {
                        pushModelViewMatrix();
                        mat4.translate(modelViewMatrix, [i * 1.9, -0.1, j * 1.9], modelViewMatrix);
                        mat4.scale(modelViewMatrix, [0.1, 1.0, 0.1], modelViewMatrix);
                        uploadModelViewMatrixToShader();
                        drawCube(0.72, 0.53, 0.04, 1.0); // argument sets brown color
                        popModelViewMatrix();
                    }
                }
            }
    
            function draw() {
                gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                mat4.perspective(60, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, projectionMatrix);
                mat4.identity(modelViewMatrix);
                mat4.lookAt([8, 5, -10], [0, 0, 0], [0, 1, 0], modelViewMatrix);
                uploadModelViewMatrixToShader();
                uploadProjectionMatrixToShader();
    
                // Draw floor in red color
                drawFloor(1.0, 0.0, 0.0, 1.0);
    
                // Draw table
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 1.1, 0.0], modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawTable(); //Call drawTable() function
                popModelViewMatrix();
    
                // Draw box on top of the table
                pushModelViewMatrix();
                mat4.translate(modelViewMatrix, [0.0, 2.7, 0.0], modelViewMatrix);
                mat4.scale(modelViewMatrix, [0.5, 0.5, 0.5], modelViewMatrix);
                uploadModelViewMatrixToShader();
                drawCube(0.0, 0.0, 1.0, 1.0);
                popModelViewMatrix()
    
            }
    
            function startup() {
                canvas = document.getElementById("myGLCanvas");
                gl = WebGLDebugUtils.makeDebugContext(createGLContext(canvas));
                setupShaders();
                setupBuffers();
                gl.clearColor(1.0, 1.0, 1.0, 1.0);
                gl.enable(gl.DEPTH_TEST);
                draw();
            }
        </script>
    </head>
    <body onload="startup();">
        <canvas id="myGLCanvas" width="500" height="500"></canvas>
    </body>
</html>

1 个答案:

答案 0 :(得分:0)

您可以使用loadTexture()方法在WebGL项目中加载纹理。

  

loadTexture()例程通过创建WebGL纹理对象开始   通过调用WebGL createTexture()函数来构造纹理。然后上传   使用texImage2D()的单个蓝色像素。这使得纹理   即使可能需要一些,也可立即用作纯蓝色   我们的图片下载的时刻。

     

要从图像文件加载纹理,它会创建一个图像   对象并将src分配给我们希望用作图像的URL   我们的质地。我们将调用我们分配给image.onload的函数   图像下载完成后。那时,我们再次打电话   texImage2D()这次使用图像作为纹理的来源。   之后,我们根据纹理设置过滤和包装   我们下载的图像是否都是2的幂   尺寸与否。

function loadTexture(gl, url) {
  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // Because images have to be download over the internet
  // they might take a moment until they are ready.
  // Until then put a single pixel in the texture so we can
  // use it immediately. When the image has finished downloading
  // we'll update the texture with the contents of the image.
  const level = 0;
  const internalFormat = gl.RGBA;
  const width = 1;
  const height = 1;
  const border = 0;
  const srcFormat = gl.RGBA;
  const srcType = gl.UNSIGNED_BYTE;
  const pixel = new Uint8Array([0, 0, 255, 255]);  // opaque blue
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                width, height, border, srcFormat, srcType,
                pixel);

  const image = new Image();
  image.onload = function() {
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                  srcFormat, srcType, image);

    // WebGL1 has different requirements for power of 2 images
    // vs non power of 2 images so check if the image is a
    // power of 2 in both dimensions.
    if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
       // Yes, it's a power of 2. Generate mips.
       gl.generateMipmap(gl.TEXTURE_2D);
    } else {
       // No, it's not a power of 2. Turn of mips and set
       // wrapping to clamp to edge
       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    }
  };
  image.src = url;

  return texture;
}

或者在git hub上查看这个cube with texture WebGL tutorial