简体   繁体   English

为什么这个 WebGL 代码没有在 React Native 上渲染一个正方形?

[英]Why doesn't this WebGL code render a square on React Native?

To give some context I'm trying to implement Conway's Game of Life as a mobile app using React Native and WebGL (the expo-gl module);为了提供一些背景信息,我正在尝试使用 React Native 和 WebGL(expo-gl 模块)将 Conway 的 Game of Life 实现为移动应用程序; the included code is from some of the modules of this project and targets its rendering to a component on the app's main screen.包含的代码来自该项目的一些模块,并将其渲染定位到应用程序主屏幕上的组件。 I've verified that the WebGL context gets initialised by testing if I can clear the GLView to a desired colour.我已经通过测试是否可以将 GLView 清除为所需的颜色来验证 WebGL 上下文是否已初始化。 I've used the getShaderParameter and getProgramParameter functions to confirm that the GL shader program has compiled and linked (and passes validation).我使用了 getShaderParameter 和 getProgramParameter 函数来确认 GL 着色器程序已编译和链接(并通过验证)。 I've used the getError function to confirm that the GL error flag doesn't get set.我使用 getError function 来确认没有设置 GL 错误标志。

The app applies the composite model space to clip space transform (that is passed to the shader program) to the vertices of the model in the JS program and outputs the result to the console.该应用程序将复合 model 空间应用于剪辑空间变换(即传递给着色器程序)到 JS 程序中 model 的顶点,并将结果输出到控制台。 In my understanding of OpenGL this results in a set of vertices that should be renderable, as applying the "divide by w" operation results in vertices with x, y and z components that are all in the range [-1, 1].根据我对 OpenGL 的理解,这会导致一组顶点应该是可渲染的,因为应用“除以 w”操作会导致顶点的 x、y 和 z 分量都在 [-1, 1] 范围内。 However, nothing gets rendered.但是,没有渲染任何内容。 Below is the main JS module in question.下面是有问题的主要 JS 模块。

// This module contains functions that implement the rendering of the game board by determining
// the contents of the corresponding GLView component for each frame.

import {matrix, multiply} from 'mathjs';
import vertexShader from './VertexShader.js';
import fragmentShader from './FragmentShader.js';
import {cellModel, verticalLineModel, horizontalLineModel, modelElements} from './GameBoardModels.js';

// These global variables are assigned values related to the OpenGL context that will be needed to
// render the game board each frame.
var gl;
const glParameters = {
  uniform_modToClip: 0,
  uniform_colour: 0,
  attribute_modPosition: 0,
  vertexBuffer_cellModel: 0,
  vertexBuffer_verticalLineModel: 0,
  vertexBuffer_horizontalLineModel: 0,
  elementBuffer: 0
};

const frontendState = {
  mode: 0,
  cameraPosition: {
    x: 0, y: 0, z: -2
  }
};

// This function generates a 4 - vector translation matrix.
function translate(x, y, z) {
  return (
    matrix([[1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z], [0, 0, 0, 1]])
  );
}

// This function returns a function that is used to generate the transformation matrix that is
// passed to the vertex shader for each model rendered.
function genModelTransformFunction(x, y, z, frustumScale0, frustumScale1, zNear, zFar) {
  const worldToCamera = translate(x, y, z);
  const cameraToClip = matrix([[frustumScale0, 0, 0, 0], [0, frustumScale1, 0, 0], [0, 0,
                                ((zFar + zNear) / (zNear - zFar)),
                                ((2 * zFar * zNear) / (zNear - zFar))], [0, 0, -1, 0]]);
  const worldToClip = multiply(cameraToClip, worldToCamera);

  function modelTransformFunction(i, j) {
    const modelToWorld = translate(i, j, 0);
    const modelToClip = multiply(worldToClip, modelToWorld);
    return modelToClip;
  }

  return modelTransformFunction;
}

// This function is called when the corresponding GLView component is first rendered in App.
// It creates the single GL shader program used for rendering the game board and assigns values
// related to the GL context to the global variables gl and glParameters.
function onContextCreation(_gl) {
  _gl.viewport(0, 0, _gl.drawingBufferWidth, _gl.drawingBufferHeight);
  _gl.clearColor(1, 1, 1, 1);
  _gl.clear(_gl.COLOR_BUFFER_BIT);

  const vert = _gl.createShader(_gl.VERTEX_SHADER);
  _gl.shaderSource(vert, vertexShader);
  _gl.compileShader(vert);

  const frag = _gl.createShader(_gl.FRAGMENT_SHADER);
  _gl.shaderSource(frag, fragmentShader);
  _gl.compileShader(frag);

  const shaderProgram = _gl.createProgram();
  _gl.attachShader(shaderProgram, vert);
  _gl.attachShader(shaderProgram, frag);
  _gl.linkProgram(shaderProgram);
  _gl.validateProgram(shaderProgram);
  const vertStatus = _gl.getShaderParameter(vert, _gl.COMPILE_STATUS);
  const fragStatus = _gl.getShaderParameter(frag, _gl.COMPILE_STATUS);
  const linkStatus = _gl.getProgramParameter(shaderProgram, _gl.LINK_STATUS);
  const programValid = _gl.getProgramParameter(shaderProgram, _gl.VALIDATE_STATUS);
  console.log("Vertex shader status: " + vertStatus);
  console.log("Fragment shader status: " + fragStatus);
  console.log("Program link status: " + linkStatus);
  console.log("Program validate status: " + programValid);

  gl = _gl;
  glParameters.uniform_modToClip = _gl.getUniformLocation(shaderProgram, "modToClip");
  glParameters.uniform_colour = _gl.getUniformLocation(shaderProgram, "colour");
  glParameters.attribute_modPosition = _gl.getAttribLocation(shaderProgram, "modPosition");
  glParameters.vertexBuffer_cellModel = loadBuffer(cellModel, null);
  glParameters.vertexBuffer_verticalLineModel = loadBuffer(verticalLineModel, null);
  glParameters.vertexBuffer_horizontalLineModel = loadBuffer(horizontalLineModel, null);
  glParameters.elementBuffer = loadBuffer(null, modelElements);
  gl.useProgram(shaderProgram);

  setInterval(handleRenderEvent, 16.67, frontendState.mode, frontendState.cameraPosition);

}

// This function loads vertex and element drawing data into GL buffers, which will be used to
// perform the rendering for each frame.
function loadBuffer(vertexArray, elementArray) {
  let buffer;
  if (elementArray === null) {
    buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexArray), gl.STREAM_DRAW);
  }
  else {
    buffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elementArray), gl.STREAM_DRAW);
  }

  return buffer;
}

function handleRenderEvent(mode, cameraPosition) {
  const transformFunction = genModelTransformFunction(cameraPosition.x, cameraPosition.y, cameraPosition.z, 1, 1, 0.5, 100);
  renderGameBoard(transformFunction);
}

function renderGameBoard(transformFunction) {
  const testTransform = transformFunction(0, 0);
  gl.bindBuffer(gl.ARRAY_BUFFER, glParameters.vertexBuffer_cellModel);
  gl.vertexAttribPointer(glParameters.attribute_modPosition, 4, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(glParameters.attribute_modPosition);

  gl.uniformMatrix4fv(glParameters.uniform_modToClip, false, testTransform);
  gl.uniform4fv(glParameters.uniform_colour, new Float32Array([0, 0, 1, 1]));

  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, glParameters.elementBuffer);
  gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
  gl.flush();
  gl.endFrameEXP();
  
  if (frontendState.mode === 0) {
    console.log("\ntransform: " + JSON.stringify(testTransform));
    const errorLog = gl.getError();
    console.log("\n:GL error log: " + errorLog);
    
    const cellModel0 = matrix([0, 0, 0, 1]);
    const cellModel1 = matrix([1, 0, 0, 1]);
    const cellModel2 = matrix([1, 1, 0, 1]);
    const cellModel3 = matrix([0, 1, 0, 1]);

    const cellModelClip0 = multiply(testTransform, cellModel0);
    const cellModelClip1 = multiply(testTransform, cellModel1);
    const cellModelClip2 = multiply(testTransform, cellModel2);
    const cellModelClip3 = multiply(testTransform, cellModel3);

    console.log("\n\ncellModelClip0: " + JSON.stringify(cellModelClip0));
    console.log("\ncellModelClip1: " + JSON.stringify(cellModelClip1));
    console.log("\ncellModelClip2: " + JSON.stringify(cellModelClip2));
    console.log("\ncellModelClip3: " + JSON.stringify(cellModelClip3));
    frontendState.mode = 1;

    
  }

}

export {onContextCreation};

// The GL vertex shader has its own module for clarity.

const vertexShader = `
attribute vec4 modPosition;
uniform mat4 modToClip;

void main(void) {
  gl_Position = modToClip * modPosition;
}
`

export default vertexShader;

// The GL fragment shader has its own module for clarity.

const fragmentShader = `
uniform highp vec4 colour;

void main(void) {
  gl_FragColor = colour;
}
`

export default fragmentShader;

// This module contains the vertex data used within GameBoardRenderer to render models.

const cellModel = [0, 0, 0, 1,
                   1, 0, 0, 1,
                   1, 1, 0, 1,
                   0, 1, 0, 1];

const verticalLineModel = [-128, -0.1, 0, 1,
                           128, -0.1, 0, 1,
                           128, 0.1, 0, 1,
                           -128, 0.1, 0, 1];

const horizontalLineModel = [-0.1, -128, 0, 1,
                             0.1, -128, 0, 1,
                             0.1, 128, 0, 1,
                             -0.1, 128, 0, 1];

const modelElements = [0, 1, 2, 0, 2, 3];

export {cellModel, verticalLineModel, horizontalLineModel, modelElements};

The console log of the transform test is as follows.转换测试的控制台日志如下。

cellModelClip0: {"mathjs":"DenseMatrix","data":[0,0,1.0150753768844223,2],"size":[4]}

cellModelClip1: {"mathjs":"DenseMatrix","data":[1,0,1.0150753768844223,2],"size":[4]}

cellModelClip2: {"mathjs":"DenseMatrix","data":[1,1,1.0150753768844223,2],"size":[4]}

cellModelClip3: {"mathjs":"DenseMatrix","data":[0,1,1.0150753768844223,2],"size":[4]}

Can anyone see what I've done wrong here?谁能看到我在这里做错了什么? Thanks.谢谢。

It turns out the transform was correct but I was passing it to the shader program using the wrong data type, which failed silently.事实证明转换是正确的,但我使用错误的数据类型将它传递给着色器程序,但它默默地失败了。 The uniformMatrix4fv function works as intended if passed a 1 dimensional standard Javascript array but not if passed a 2 dimensional matrix from the mathjs library.如果通过一维标准 Javascript 数组,则 uniformMatrix4fv function 可以按预期工作,但如果通过 mathjs 库中的二维矩阵则不能。 However, accoring to this tutorial it does work if passed a value of type mat4 from the glMatrix library.但是,根据本教程,如果从 glMatrix 库中传递 mat4 类型的值,它确实可以工作。 This is why I'd assumed it would be able to iterate over the matrix type from mathjs that I'd been using.这就是为什么我假设它能够从我一直使用的 mathjs 迭代矩阵类型。 I've included below the git diff that made this work.我已经在 git diff 下方包含了使这项工作的差异。

   function modelTransformFunction(i, j) {
    const modelToWorld = translate(i, j, 0);
    const modelToClip = multiply(worldToClip, modelToWorld);
-    return modelToClip;
+    let index = 0;
+    const modelToClipArray = Array(16).fill(0);
+    modelToClip.forEach((value) => {modelToClipArray[index] = value;
+                                    index++;});
+    return modelToClipArray;
+  }

  return modelTransformFunction;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM