【发布时间】:2020-11-18 10:44:03
【问题描述】:
我正在渲染 2 个平面,每个平面具有 2 个混合纹理。但是当我清空一个纹理时,就像 GPU 用其他应用程序纹理替换缺少的纹理,即使是前一个平面的纹理。 我在着色器中编写了一个解决方案,包括检测 sampler2D textureN 是否已启动:
void main()
{
vec4 finalPx = vec4(0.0, 0.0, 0.0, 1.0);
if(has_tex_1 > 0.0)
{
finalPx += texture2D(tex_1, uv_1) * 0.5;
}
if(has_tex_2 > 0.0)
{
finalPx += texture2D(tex_2, uv_2) * 0.5;
}
gl_FragColor = finalPx;
}
...但我担心我的解决方案可能会让我以后的着色器编程变得困难,我认为 webgl 应该负责尊重代码逻辑:如果应用程序没有加载纹理,着色器应该什么也不加载。
这是我的应用程序:
const PICTURES = {
indices: [
0, 1, 2,
0, 3, 1,
],
vertices: [
1.000000, -1.000000, 0.000000,
-1.000000, 1.000000, 0.000000,
-1.000000, -1.000000, 0.000000,
1.000000, 1.000000, 0.000000,
],
uv_layers: {
'uv_1': [
1.000000, 1.077309,
0.000000, -0.077309,
0.000000, 1.077309,
1.000000, -0.077309,
],
'uv_2': [
1.500000, 1.500000,
-0.500000, -0.500000,
-0.500000, 1.500000,
1.500000, -0.500000,
],
},
};
const vertexShaderCode = `
precision mediump float;
attribute vec4 vertex;
attribute vec2 uv;
varying vec2 UV;
attribute vec2 uv2;
varying vec2 UV2;
uniform mat4 projectionMatrix, viewMatrix, modelMatrix;
void main()
{
UV = uv;
UV2 = uv2;
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vertex;
}`;
const fragmentShaderCode = `
precision mediump float;
varying vec2 UV;
varying vec2 UV2;
uniform sampler2D bitmap_1;
uniform sampler2D bitmap_2;
void main() {
// Mix the bitmaps in equal proportions
vec4 px1 = texture2D(bitmap_1, UV) * 0.5;
vec4 px2 = texture2D(bitmap_2, UV2) * 0.5;
gl_FragColor = px1 + px2;
}`;
function loadShader(gl, vertexShaderCode, fragmentShaderCode) {
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderCode);
gl.compileShader(vertexShader);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(vertexShader));
gl.deleteShader(vertexShader);
return null;
}
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderCode);
gl.compileShader(fragmentShader);
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(fragmentShader));
gl.deleteShader(fragmentShader);
return null;
}
return [vertexShader, fragmentShader];
}
function loadTexture(gl, url) {
function isPowerOf2(value){ return (value & (value - 1)) == 0; }
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([1.0, 0.0, 1.0, 1.0]); // magenta to warn if there is no texture
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;
}
function Material(gl, shaders) {
this.shaderProgram = gl.createProgram();
gl.attachShader(this.shaderProgram, shaders[0]); // Vertex shader
gl.attachShader(this.shaderProgram, shaders[1]); // Fragment shader
gl.linkProgram(this.shaderProgram);
if (!gl.getProgramParameter(this.shaderProgram, gl.LINK_STATUS)) {
alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(this.shaderProgram));
return null;
}
}
function main() {
var surface = document.getElementById('glcanvas');
var gl = surface.getContext('webgl');
if (!gl) {
alert('Unable to initialize WebGL. Your browser or machine may not support it.');
return;
}
// One material for two textures maped within its own uv maps, like any 3D software could allow
let shaders = loadShader(gl,vertexShaderCode, fragmentShaderCode);
// -------------------------------------mesh 1---------------------------------------
// Create the index buffer
const indexBuffer_1 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer_1);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(PICTURES.indices), gl.STATIC_DRAW);
// The vertex buffer
const vertexBuffer_1 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer_1);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.vertices), gl.STATIC_DRAW);
// uv layers buffers (two uv channels for two different bitmaps)
const uvBuffer_1_A = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer_1_A);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.uv_layers['uv_1']), gl.STATIC_DRAW);
const uvBuffer_1_B = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer_1_B);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.uv_layers['uv_2']), gl.STATIC_DRAW);
const material_1 = new Material(gl, shaders);
// Create the textures for the respectives uv buffers
const texture_1 = loadTexture(gl, TEXTURES_FOLDER+'old_fashioned_portrait_1.jpg');
const texture_2 = loadTexture(gl, TEXTURES_FOLDER+'demon.jpg');
// Transform data
let modelMatrix_1 = mat4.create();
mat4.translate(modelMatrix_1, modelMatrix_1, [-1.5, 0.0, 0.0]);
let meshData_1 = {
indices : PICTURES.indices,
indexBuffer : indexBuffer_1,
vertexBuffer: vertexBuffer_1,
uvBuffer_A : uvBuffer_1_A,
uvBuffer_B : uvBuffer_1_B,
material : material_1,
texture_A : texture_1,
texture_B : texture_2,
modelMatrix : modelMatrix_1,
};
// -------------------------------------mesh 2---------------------------------------
// Create the index buffer
const indexBuffer_2 = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer_2);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(PICTURES.indices), gl.STATIC_DRAW);
// The vertex buffer
const vertexBuffer_2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer_2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.vertices), gl.STATIC_DRAW);
// uv layers buffers (two uv channels for two different bitmaps)
const uvBuffer_2_A = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer_2_A);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.uv_layers['uv_1']), gl.STATIC_DRAW);
const uvBuffer_2_B = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer_2_B);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(PICTURES.uv_layers['uv_2']), gl.STATIC_DRAW);
const material_2 = new Material(gl, shaders);
const texture_3 = loadTexture(gl, TEXTURES_FOLDER+'old_fashioned_portrait_2.jpg');
const texture_4 = loadTexture(gl, TEXTURES_FOLDER+'phantom_girl.jpg');
// Transform data
let modelMatrix_2 = mat4.create();
mat4.translate(modelMatrix_2, modelMatrix_2, [1.5, 0.0, 0.0]);
let meshData_2 = {
indices : PICTURES.indices,
indexBuffer : indexBuffer_2,
vertexBuffer: vertexBuffer_2,
uvBuffer_A : uvBuffer_2_A,
uvBuffer_B : uvBuffer_2_B,
material : material_2,
texture_A : texture_3,
texture_B : texture_4,
modelMatrix : modelMatrix_2,
};
// Render data and methods
requestAnimationFrame(function () {render(gl, meshData_1, meshData_2, Date.now()*0.001);});
}
function render(gl, meshData_1, meshData_2, before)
{
var now = Date.now()*0.001;
var delta = now-before;
rotation += delta;
// ------------------------------------canvas----------------------------------------
gl.clearColor(0.0, 0.5, 0.5, 1.0);
gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const fieldOfView = 45 * Math.PI / 180;
const zNear = 0.1;
const zFar = 100.0;
let projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
let viewMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, [0.0, 0.0, -5.0]);
// -------------------------------------mesh 1---------------------------------------
gl.useProgram(meshData_1.material.shaderProgram);
// Vertices transformations
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_1.material.shaderProgram, 'projectionMatrix'), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_1.material.shaderProgram, 'viewMatrix'), false, viewMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_1.material.shaderProgram, 'modelMatrix'), false, meshData_1.modelMatrix);
// Give the GPU the order of the points to form the triangles
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshData_1.indexBuffer);
// Give the GPU the transformed vertices
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_1.vertexBuffer);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_1.material.shaderProgram, 'vertex'), 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_1.material.shaderProgram, 'vertex'));
// First texture mapped in the triangles with the first uv coordinates
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_1.uvBuffer_A);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_1.material.shaderProgram, 'uv'), 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_1.material.shaderProgram, 'uv'));
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, meshData_1.texture_A);
gl.uniform1i(gl.getUniformLocation(meshData_1.material.shaderProgram, 'bitmap_1'), 0);
// Second texture mapped in the triangles with the second uv coordinates
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_1.uvBuffer_B);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_1.material.shaderProgram, 'uv2'), 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_1.material.shaderProgram, 'uv2'));
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, meshData_1.texture_B);
gl.uniform1i(gl.getUniformLocation(meshData_1.material.shaderProgram, 'bitmap_2'), 1);
gl.drawElements(gl.TRIANGLES, meshData_1.indices.length, gl.UNSIGNED_SHORT, 0);
gl.useProgram(null);
// -------------------------------------mesh 2---------------------------------------
gl.useProgram(meshData_2.material.shaderProgram);
// Vertices transformations
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_2.material.shaderProgram, 'projectionMatrix'), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_2.material.shaderProgram, 'viewMatrix'), false, viewMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(meshData_2.material.shaderProgram, 'modelMatrix'), false, meshData_2.modelMatrix);
// Give the GPU the order of the points to form the triangles
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, meshData_2.indexBuffer);
// Give the GPU the transformed vertices
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_2.vertexBuffer);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_2.material.shaderProgram, 'vertex'), 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_2.material.shaderProgram, 'vertex'));
// First texture mapped in the triangles with the first uv coordinates
我在继续代码之前指出了问题:
如果我评论下一个纹理渲染就像应用程序(GPU?)用前一个平面的第一个纹理提供变量“uniform sampler2D bitmap_1”:“old_fashioned_portrait_1.jpg”
/*
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_2.uvBuffer_A);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_2.material.shaderProgram, 'uv'), 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_2.material.shaderProgram, 'uv'));
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, meshData_2.texture_A);
gl.uniform1i(gl.getUniformLocation(meshData_2.material.shaderProgram, 'bitmap_1'), 0);
*/
// Second texture mapped in the triangles with the second uv coordinates
gl.bindBuffer(gl.ARRAY_BUFFER, meshData_2.uvBuffer_B);
gl.vertexAttribPointer(gl.getAttribLocation(meshData_2.material.shaderProgram, 'uv2'), 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl.getAttribLocation(meshData_2.material.shaderProgram, 'uv2'));
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, meshData_2.texture_B);
gl.uniform1i(gl.getUniformLocation(meshData_2.material.shaderProgram, 'bitmap_2'), 1);
gl.drawElements(gl.TRIANGLES, meshData_2.indices.length, gl.UNSIGNED_SHORT, 0);
gl.useProgram(null);
requestAnimationFrame(function(){render(gl, meshData_1, meshData_2, now);});
}
main();
【问题讨论】:
标签: javascript 3d glsl webgl textures