You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebGL2实例化元素无法渲染问题求助

WebGL2实例化元素渲染无显示问题排查

在WebGL2实现实例化元素渲染时,元素无法正常显示,调用GL.getError()未返回任何报错信息,已确认缓冲区设置正确,相关代码如下:

const Canvas = document.getElementById("game_ui");
Canvas.width = window.innerWidth;
Canvas.height = window.innerHeight;
const GL = Canvas.getContext("webgl2");

if(!GL) 
{
    document.body.innerHTML = "Could not create Webgl2 context.";
}

class Program
{
    
    constructor()
    {
        this.Identity = GL.createProgram();
        this.Shaders = [];
    }
    
    CreateShader(type, glsl_string)
    {
        var shader = GL.createShader(type);
        GL.shaderSource(shader, glsl_string);
        GL.compileShader(shader);
        
        if(!GL.getShaderParameter(shader, GL.COMPILE_STATUS))
        {
            throw new Error(GL.getShaderInfoLog(shader));
        }
        else
        {
            this.Shaders.push(shader);
        }
        
    }
    
    CreateProgram()
    {
    
        for(var shader of this.Shaders)
        {
            GL.attachShader(this.Identity, shader);
        }
        GL.linkProgram(this.Identity);
        
        if(!GL.getProgramParameter(this.Identity, GL.LINK_STATUS))
        {
            throw new Error(GL.getProgramInfoLog(this.Identity));
        }
        
    }
    
}

class Buffer
{
    constructor(type, buffer, shader_location)
    {
        this.Type = type;
        this.Identity = buffer;
        this.attributeLocation = shader_location;
    }
}


const ADD_COLOR = 0;
const TEXTURE = 1;
class VertexArrayObject
{

    constructor()
    {
        this.Identity = GL.createVertexArray();
        this.Buffers = [];
        this.Uniforms = [];
        
        GL.bindVertexArray(this.Identity);
    }
    
    CreateBuffer(type, data, behavior)
    {
        var buffer = GL.createBuffer(type);
        GL.bindBuffer(type, buffer);
        GL.bufferData(type, new Uint16Array(data), behavior);
        
        return buffer;
    }
    
    CreateAttribBuffer(program, variable, type)
    {
        var buffer = GL.createBuffer(type);
        GL.bindBuffer(type, buffer);
        
        var attrib = GL.getAttribLocation(program, variable);
        
        this.Buffers.push(new Buffer(type, buffer, attrib));
    }
    
    SetAttribBufferData(buffer_array_index, data, amount_per_vertex, divisor, behavior)
    {
        GL.enableVertexAttribArray(this.Buffers[buffer_array_index].attributeLocation);
        GL.bindBuffer(this.Buffers[buffer_array_index].Type, this.Buffers[buffer_array_index].Identity);
        GL.bufferData(this.Buffers[buffer_array_index].Type, new Float32Array(data), behavior);
        GL.vertexAttribPointer(this.Buffers[buffer_array_index].attributeLocation, amount_per_vertex, GL.FLOAT, false, 0, 0);
        GL.vertexAttribDivisor(this.Buffers[buffer_array_index].attributeLocation, divisor);
    }
    
    CreateUniform(program, variable)
    {
        this.Uniforms.push(GL.getUniformLocation(program, variable));
    }
    
    SetImageUnit(unit)
    {
        GL.uniform1i(this.Uniforms[TEXTURE], unit);
    }
    
    SetColorAddition(red, green, blue)
    {
        GL.uniform3f(this.Uniforms[ADD_COLOR], red, green, blue);
    }

}

var shaderProgram = new Program();
shaderProgram.CreateShader(GL.VERTEX_SHADER, vertexShader);
shaderProgram.CreateShader(GL.FRAGMENT_SHADER,fragmentShader);
shaderProgram.CreateProgram();
GL.useProgram(shaderProgram.Identity);

const DEFAULT_POSITION_SIZE = 0;
const TEXTURE_COORDS = 1;
const OFFSET = 2;
const SCALE = 3;
const CROP = 4;
const OPACITY = 5;

var vertexArrayObject = new VertexArrayObject();
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "defaultPositionSize", GL.ARRAY_BUFFER);
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "textureCoord", GL.ARRAY_BUFFER);
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "Offset", GL.ARRAY_BUFFER);
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "Scale", GL.ARRAY_BUFFER);
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "Crop", GL.ARRAY_BUFFER);
vertexArrayObject.CreateAttribBuffer(shaderProgram.Identity, "Opacity", GL.ARRAY_BUFFER);
vertexArrayObject.CreateUniform(shaderProgram.Identity, "addColor");
vertexArrayObject.CreateUniform(shaderProgram.Identity, "textureSample");
vertexArrayObject.SetColorAddition(0, 0, 0);

class UI
{
    
    constructor()
    {
        this.Texture = null;
        this.Opacity = [];
        this.Offset = [];
        this.Scale = [];
        this.Crop = [];
    }
    
    CreateTexture(image_source)
    {
        this.Texture = GL.createTexture();
        
        var image = document.createElement("img");
        image.src = "Images/" + image_source + ".png";
        
        image.onload = () =>
        {
            GL.bindTexture(GL.TEXTURE_2D, this.Texture);
            GL.texImage2D(GL.TEXTURE_2D, 0, GL.RGBA, GL.RGBA, GL.UNSIGNED_BYTE, image);
            
            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.NEAREST);
            GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
        };
        
        GL.bindTexture(GL.TEXTURE_2D, null);
    }
    
    CreateElements(opacity, offset, scale, crop)
    {
        this.Opacity = this.Opacity.concat(opacity);
        this.Offset = this.Offset.concat(offset);
        this.Scale = this.Scale.concat(scale);
        this.Crop = this.Crop.concat(crop);
    }
    
    DeleteElements(indices)
    {
        
        for(var index of indices)
        {
            this.Opacity.splice(index, 1);
            this.Offset.splice(index * 2, 2);
            this.Scale.splice(index * 2, 2);
            this.Crop.splice(index * 4, 4);
        }
        
    }
    
    DeleteUI()
    {
        GL.deleteTexture(this.Texture);
    }
    
}

GL.enable(GL.DEPTH_TEST);
GL.enable(GL.BLEND);
GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);
GL.activeTexture(GL.TEXTURE0);
vertexArrayObject.SetImageUnit(0);

var ui = new UI();
ui.CreateTexture("test");
ui.CreateElements([1.0], [0.5, 0.5], [0.5, 0.5], [0, 0, 1, 1]);

GL.bindTexture(GL.TEXTURE_2D, ui.Texture);
vertexArrayObject.SetAttribBufferData(DEFAULT_POSITION_SIZE, [-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0], 2, 0, GL.STATIC_DRAW);
vertexArrayObject.SetAttribBufferData(TEXTURE_COORDS, [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0], 2, 0, GL.STATIC_DRAW);
vertexArrayObject.SetAttribBufferData(OFFSET, ui.Offset, 2, 1, GL.DYNAMIC_DRAW);
vertexArrayObject.SetAttribBufferData(CROP, ui.Crop, 4, 1, GL.DYNAMIC_DRAW);
vertexArrayObject.SetAttribBufferData(SCALE, ui.Scale, 2, 1, GL.DYNAMIC_DRAW);
vertexArrayObject.SetAttribBufferData(OPACITY, ui.Opacity, 1, 1, GL.DYNAMIC_DRAW);
var indicesBuffer = vertexArrayObject.CreateBuffer(GL.ELEMENT_ARRAY_BUFFER, [0, 1, 2, 1, 2, 3], GL.STATIC_DRAW);
function Update()
{
    GL.clear(GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
        
    GL.drawElementsInstanced(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0, ui.Opacity.length);
    requestAnimationFrame(Update);
}

requestAnimationFrame(Update);

排查方向

  • 纹理加载异步问题:图片加载是异步操作,Update函数启动时纹理可能还未完成初始化,导致采样无效。需等待图片加载完成后再开始渲染,或初始化一个1x1的默认纹理作为占位。
  • VAO未绑定:绘制前未绑定顶点数组对象(VAO),WebGL无法读取顶点和实例化属性数据。需在GL.drawElementsInstanced前添加GL.bindVertexArray(vertexArrayObject.Identity)。
  • 元素缓冲区未关联VAO:创建ELEMENT_ARRAY_BUFFER时,VAO已解绑,导致索引数据未被VAO记录。需在VAO绑定状态下创建并绑定索引缓冲区,可修改CreateBuffer方法确保在VAO绑定期间执行,或在创建索引缓冲区前重新绑定VAO。
  • 视口未设置:Canvas尺寸设置后未调用GL.viewport(0, 0, Canvas.width, Canvas.height),导致渲染内容与画布尺寸不匹配,可能被偏移或裁剪到不可见区域。
  • 着色器属性计算错误:检查顶点着色器中是否正确使用实例化属性(Offset、Scale等)计算最终顶点位置。例如默认顶点坐标为[-1,1],叠加Offset[0.5,0.5]后,需确认最终坐标是否在WebGL的裁剪空间[-1,1]范围内,超出则无法显示。

内容的提问来源于stack exchange,提问作者Youaregreat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 20:44:53