- 当前 Bug 的表现(可附上截图)
使用webgl渲染时,调用 gl.uniform3fv, gl.uniform4fv 等接口设置shader参数时发现以下问题:
以 gl.uniform3fv(dest, v) 为例,如果 v = Float32Array([1,2,3]).subarray(1,2) ,即输入的参数是一个byteoffset != 0 的TypeArray,那么实际运行时该参数的设置结果是错误的,导致游戏显示出现各种问题。
- 预期表现
- 复现路径
我们还试过华为,三星的多款Android的手机,均有这个问题。
- 提供一个最简复现 Demo
测试代码:
let ctx = canvas.getContext('webgl')
let HEAP32 = new Int32Array(1024 * 1024 * 8)
let global_time = 0;
var aspectRatio;
var vertexArray;
var vertexBuffer;
var vertexNumComponents;
var vertexCount;
var uScalingFactor;
var uGlobalColor;
var uRotationVector;
var aVertexPosition;
var previousTime = 0.0;
var degreesPerSecond = 90.0;
var HEAP = new Float32Array(16);
var currentRotation = 8;
var currentScale = 10;
var currentAngle = 0;
var shaderProgram = null;
var rotationRate = 0;
function compileShader(code, type) {
let gl = ctx;
var shader = gl.createShader(type);
gl.shaderSource(shader, code);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(`Error compiling ${type === gl.VERTEX_SHADER ? "vertex" : "fragment"} shader:`);
console.log(gl.getShaderInfoLog(shader));
}
return shader;
}
function animateScene() {
let gl = ctx;
gl.viewport(0, 0, 320, 528);
gl.clearColor(0.8, 0.9, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var radians = currentAngle * Math.PI / 180.0;
HEAP[currentRotation] = Math.sin(radians);
HEAP[currentRotation + 1] = Math.cos(radians);
gl.useProgram(shaderProgram);
uScalingFactor = gl.getUniformLocation(shaderProgram, "uScalingFactor");
uGlobalColor = gl.getUniformLocation(shaderProgram, "uGlobalColor");
uRotationVector = gl.getUniformLocation(shaderProgram, "uRotationVector");
gl.uniform2fv(uScalingFactor, HEAP.subarray(currentScale, currentScale + 2));
gl.uniform2fv(uRotationVector, HEAP.subarray(currentRotation, currentRotation + 2));
var rot = gl.getUniform(shaderProgram, uRotationVector);
if (rot[0] != HEAP[currentRotation] || rot[1] != HEAP[currentRotation + 1])
console.log("buggy");
else
console.log("ok");
gl.uniform4fv(uGlobalColor, [0.1, 0.7, 0.2, 1.0]);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(aVertexPosition);
gl.vertexAttribPointer(aVertexPosition, vertexNumComponents, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
window.requestAnimationFrame(function (currentTime) {
var deltaAngle = ((currentTime - previousTime) / 1000.0) * degreesPerSecond;
currentAngle = (currentAngle + deltaAngle) % 360;
previousTime = currentTime;
animateScene();
});
}
/**
* 游戏主函数
*/
export default class Main {
constructor() {
// 维护当前requestAnimationFrame的id
this.aniId = 0
console.log("start !!!!!")
this.startup();
}
startup() {
let gl = ctx;
var shaderSet = [
{
type: gl.VERTEX_SHADER,
code: "attribute vec2 aVertexPosition;" +
"uniform vec2 uScalingFactor;" +
"uniform vec2 uRotationVector;" +
"void main() {" +
"vec2 rotatedPosition = vec2(aVertexPosition.x * uRotationVector.y + aVertexPosition.y * uRotationVector.x," +
"aVertexPosition.y * uRotationVector.y - aVertexPosition.x * uRotationVector.x);" +
"gl_Position = vec4(rotatedPosition * uScalingFactor, 0.0, 1.0);" +
"}"
},
{
type: gl.FRAGMENT_SHADER,
code: "precision highp float;" +
"uniform vec4 uGlobalColor;" +
"void main() { gl_FragColor = uGlobalColor; }"
}
];
shaderProgram = this.buildShaderProgram(shaderSet);
aspectRatio = 320 / 528;
HEAP[currentRotation] = 0;
HEAP[currentRotation + 1] = 1;
HEAP[currentScale] = 1;
HEAP[currentScale + 1] = aspectRatio;
vertexArray = new Float32Array([
-0.5, 0.5, 0.5, 0.5, 0.5, -0.5,
-0.5, 0.5, 0.5, -0.5, -0.5, -0.5
]);
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW);
vertexNumComponents = 2;
vertexCount = vertexArray.length / vertexNumComponents;
currentAngle = 0.0;
rotationRate = 6;
animateScene();
}
buildShaderProgram(shaderInfo) {
let gl = ctx;
var program = gl.createProgram();
shaderInfo.forEach(function (desc) {
var shader = compileShader(desc.code, desc.type);
if (shader) gl.attachShader(program, shader);
});
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log("Error linking shader program:");
console.log(gl.getProgramInfoLog(program));
}
return program;
}
// 实现游戏帧循环
}
我遇到一个类似的问题,微信里面没法用x-shader。今天找到一个办法,成功在微信上渲染出来效果。
方法如下:
就这个问题,我们研究过了cocos等引擎的处理,它们的做法都是直接 new 一个新的 TypeArray 来保证实际传入的 TypeArray的byteoffset一定为0。即:
v= new Float32Array( new Float32Array([1,2,3]).subarray(1,2))
但这样以来其实是会产生一定的garbage的,因此希望知道这个问题的实际机制是什么?微信这边是否可以帮忙fix掉?