Javascript 在片段着色期间生成要从中提取值的纹理将生成正确宽度和高度的空白屏幕
我想在代码中创建一个由RGBA颜色值数组组成的纹理,并使用这些值来确定我在片段着色器中生成的瓷砖的颜色。我从为这个问题提供的顶级解决方案中得到了这个想法,并且很多代码都是这样做的: 但是,如果使用与颜色数组相对应的高度和宽度创建纹理,则不会看到任何渲染到画布的内容。如果我硬编码不同的值,我有时会得到一个图像,但该图像不会将平铺颜色放置在所需的位置,当然,它们会随着我更改viewPos变量而移动 通过对一些手工选取的值进行试错测试,似乎只有当gl.texImage2D()接收到等于2次方的高度和宽度时,我才能得到一幅图像,尽管我在文档中没有看到这方面的任何内容。32是我能产生图像的最大宽度,16是我能产生图像的最大高度。1、2、4和8也起作用。(对于我正在测试的窗口大小,纹理大小应该是27 x 20) 请注意,片段着色器仍然接收与颜色数组大小相关的uTileColorSampSize向量。我只需要对gl.texImage2D()宽度和高度值进行硬编码即可生成图像。事实上,我为制服尝试的每一个值都产生了一个图像,尽管每个图像都有不同的瓷砖颜色图案 我在下面列出了我的Gfx类的一个稍微简化的版本(原始版本有点凌乱,并且包含了许多与此问题无关的内容)。我可以想象上面的问题是186个左右,但我在下面添加了一些额外的函数,以防这些函数恰好相关Javascript 在片段着色期间生成要从中提取值的纹理将生成正确宽度和高度的空白屏幕,javascript,graphics,glsl,webgl,shader,Javascript,Graphics,Glsl,Webgl,Shader,我想在代码中创建一个由RGBA颜色值数组组成的纹理,并使用这些值来确定我在片段着色器中生成的瓷砖的颜色。我从为这个问题提供的顶级解决方案中得到了这个想法,并且很多代码都是这样做的: 但是,如果使用与颜色数组相对应的高度和宽度创建纹理,则不会看到任何渲染到画布的内容。如果我硬编码不同的值,我有时会得到一个图像,但该图像不会将平铺颜色放置在所需的位置,当然,它们会随着我更改viewPos变量而移动 通过对一些手工选取的值进行试错测试,似乎只有当gl.texImage2D()接收到等于2次方的高度和宽
class Gfx {
constructor() {
this.canvas = document.getElementById("canvas");
this.gl = canvas.getContext("webgl");
//viewPos changes as you drag your cursor across the canvas
this.x_viewPos = 0;
this.y_viewPos = 0;
}
init() {
this.resizeCanvas(window.innerWidth, window.innerHeight);
const vsSource = `
attribute vec4 aVertPos;
uniform mat4 uMVMat;
uniform mat4 uProjMat;
void main() {
gl_Position = uProjMat * uMVMat * aVertPos;
}
`;
//my tiles get drawn in the frag shader below
const fsSource = `
precision mediump float;
uniform vec2 uViewPos;
uniform vec2 uTileColorSampSize;
uniform sampler2D uTileColorSamp;
void main() {
//tile width and height are both 33px including a 1px border
const float lineThickness = (1.0/33.0);
//gridMult components will either be 0.0 or 1.0. This is used to place the grid lines
vec2 gridMult = vec2(
ceil(max(0.0, fract((gl_FragCoord.x-uViewPos.x)/33.0) - lineThickness)),
ceil(max(0.0, fract((gl_FragCoord.y-uViewPos.y)/33.0) - lineThickness))
);
//tileIndex is used to pull color data from the sampler texture
//add 0.5 due to pixel coords being off in gl
vec2 tileIndex = vec2(
floor((gl_FragCoord.x-uViewPos.x)/33.0) + 0.5,
floor((gl_FragCoord.y-uViewPos.y)/33.0) + 0.5
);
//divide by samp size as tex coords are 0.0 to 1.0
vec4 tileColor = texture2D(uTileColorSamp, vec2(
tileIndex.x/uTileColorSampSize.x,
tileIndex.y/uTileColorSampSize.y
));
gl_FragColor = vec4(
tileColor.x * gridMult.x * gridMult.y,
tileColor.y * gridMult.x * gridMult.y,
tileColor.z * gridMult.x * gridMult.y,
1.0 //the 4th rgba in our sampler is always 1.0 anyway
);
}
`;
const shader = this.buildShader(vsSource, fsSource);
this.programInfo = {
program: shader,
attribLocs: {
vertexPosition: this.gl.getAttribLocation(shader, 'aVertPos')
},
uniformLocs: {
projMat: this.gl.getUniformLocation(shader, 'uProjMat'),
MVMat: this.gl.getUniformLocation(shader, 'uMVMat'),
viewPos: this.gl.getUniformLocation(shader, 'uViewPos'),
tileColorSamp: this.gl.getUniformLocation(shader, 'uTileColorSamp'),
tileColorSampSize: this.gl.getUniformLocation(shader, 'uTileColorSampSize')
}
};
const buffers = this.initBuffers();
//check and enable OES_texture_float to allow us to create our sampler tex
if (!this.gl.getExtension("OES_texture_float")) {
alert("Sorry, your browser/GPU/driver doesn't support floating point textures");
}
this.gl.clearColor(0.0, 0.0, 0.15, 1.0);
this.gl.clearDepth(1.0);
this.gl.enable(this.gl.DEPTH_TEST);
this.gl.depthFunc(this.gl.LEQUAL);
const FOV = 45 * Math.PI / 180; // in radians
const aspect = this.gl.canvas.width / this.gl.canvas.height;
this.projMat = glMatrix.mat4.create();
glMatrix.mat4.perspective(this.projMat, FOV, aspect, 0.0, 100.0);
this.MVMat = glMatrix.mat4.create();
glMatrix.mat4.translate(this.MVMat, this.MVMat, [-0.0, -0.0, -1.0]);
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffers.position);
this.gl.vertexAttribPointer(this.programInfo.attribLocs.vertPos, 2, this.gl.FLOAT, false, 0, 0);
this.gl.enableVertexAttribArray(this.programInfo.attribLocs.vertPos);
this.glDraw();
}
//glDraw() gets called once above, as well as in every frame of my render loop
//(not included here as I have it in a seperate Timing class)
glDraw() {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
this.gl.useProgram(this.programInfo.program);
//X and Y TILE_COUNTs varrified to correspond to colorArray size in testing
//(colorArray.length = rgbaLength * X_TILE_COUNT * Y_TILE_COUNT)
//(colorArray.length = rgbaLength * widthInTiles * heightInTiles)
//(colorArray.length = 4 * 27 * 20)
let x_tileColorSampSize = X_TILE_COUNT;
let y_tileColorSampSize = Y_TILE_COUNT;
//getTileColorArray() produces a flat array of floats between 0.0and 1.0
//equal in length to rgbaLength * X_TILE_COUNT * Y_TILE_COUNT
//every 4th value is 1.0, representing tile alpha
let colorArray = this.getTileColorArray();
let colorTex = this.colorMapTexFromArray(
x_tileColorSampSize,
y_tileColorSampSize,
colorArray
);
//SO solution said to use anyting between 0 and 15 for texUnit, they used 3
//I imagine this is just an arbitrary location in memory to hold a texture
let texUnit = 3;
this.gl.activeTexture(this.gl.TEXTURE0 + texUnit);
this.gl.bindTexture(this.gl.TEXTURE_2D, colorTex);
this.gl.uniform1i(
this.programInfo.uniformLocs.tileColorSamp,
texUnit
);
this.gl.uniform2fv(
this.programInfo.uniformLocs.tileColorSampSize,
[x_tileColorSampSize, y_tileColorSampSize]
);
this.gl.uniform2fv(
this.programInfo.uniformLocs.viewPos,
[-this.x_viewPos, this.y_viewPos] //these change as you drag your cursor across the canvas
);
this.gl.uniformMatrix4fv(
this.programInfo.uniformLocs.projMat,
false,
this.projMat
);
this.gl.uniformMatrix4fv(
this.programInfo.uniformLocs.MVMat,
false,
this.MVMat
);
this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4);
}
colorMapTexFromArray(width, height, colorArray) {
let float32Arr = Float32Array.from(colorArray);
let oldActive = this.gl.getParameter(this.gl.ACTIVE_TEXTURE);
//SO solution said "working register 31, thanks", next to next line
//not sure what that means but I think they're just looking for any
//arbitrary place to store the texture?
this.gl.activeTexture(this.gl.TEXTURE15);
var texture = this.gl.createTexture();
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
this.gl.texImage2D(
this.gl.TEXTURE_2D, 0, this.gl.RGBA,
//if I replace width and height with certain magic numbers
//like 4 or 8 (all the way up to 32 for width and 16 for height)
//I will see colored tiles, though obviously they don't map correctly.
//I THINK I've only seen it work with a widths and heights that are
//a power of 2... could the issue be that I need my texture to have
//width and height equal to a power of 2?
width, height, 0,
this.gl.RGBA, this.gl.FLOAT, float32Arr
);
//use gl.NEAREST to prevent gl from blurring texture
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MAG_FILTER, this.gl.NEAREST);
this.gl.texParameteri(this.gl.TEXTURE_2D, this.gl.TEXTURE_MIN_FILTER, this.gl.NEAREST);
this.gl.bindTexture(this.gl.TEXTURE_2D, null);
this.gl.activeTexture(oldActive);
return texture;
}
//I don't think the issue would be in the functions below, but I included them anyway
resizeCanvas(baseWidth, baseHeight) {
let widthMod = 0;
let heightMod = 0;
//...some math is done here to account for some DOM elements that consume window space...
this.canvas.width = baseWidth + widthMod;
this.canvas.height = baseHeight + heightMod;
this.gl.viewport(0, 0, this.gl.canvas.width, this.gl.canvas.height);
}
initBuffers() {
const posBuff = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, posBuff);
const positions = [
-1.0, 1.0,
1.0, 1.0,
-1.0, -1.0,
1.0, -1.0,
];
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
new Float32Array(positions),
this.gl.STATIC_DRAW
);
return {
position: posBuff
};
}
buildShader(vsSource, fsSource) {
const vertShader = this.loadShader(this.gl.VERTEX_SHADER, vsSource);
const fragShader = this.loadShader(this.gl.FRAGMENT_SHADER, fsSource);
const shaderProg = this.gl.createProgram();
this.gl.attachShader(shaderProg, vertShader);
this.gl.attachShader(shaderProg, fragShader);
this.gl.linkProgram(shaderProg);
if (!this.gl.getProgramParameter(shaderProg, this.gl.LINK_STATUS)) {
console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProg));
return null;
}
return shaderProg;
}
loadShader(type, source) {
const shader = this.gl.createShader(type);
this.gl.shaderSource(shader, source);
this.gl.compileShader(shader);
if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
console.error('An error occurred compiling the shaders: ' + this.gl.getShaderInfoLog(shader));
this.gl.deleteShader(shader);
return null;
}
return shader;
}
//getTileColorArray as it appears in my code, in case you want to take a peak.
//every tileGrid[i][j] has a color, which is an array of 4 values between 0.0 and 1.0
//the fourth (last) value in tileGrid[i][j].color is always 1.0
getTileColorArray() {
let i_min = Math.max(0, Math.floor(this.x_pxPosToTilePos(this.x_viewPos)));
let i_max = Math.min(GLOBAL.map.worldWidth-1, i_min + Math.ceil(this.x_pxPosToTilePos(this.canvas.width)) + 1);
let j_min = Math.max(0, Math.floor(this.y_pxPosToTilePos(this.y_viewPos)));
let j_max = Math.min(GLOBAL.map.worldHeight-1, j_min + Math.ceil(this.y_pxPosToTilePos(this.canvas.height)) + 1);
let colorArray = [];
for (let i=i_min; i <= i_max; i++) {
for (let j=j_min; j <= j_max; j++) {
colorArray = colorArray.concat(GLOBAL.map.tileGrid[i][j].color);
}
}
return colorArray;
}
}
Gfx类{
构造函数(){
this.canvas=document.getElementById(“canvas”);
this.gl=canvas.getContext(“webgl”);
//在画布上拖动光标时,viewPos会发生变化
此.x_viewPos=0;
this.y_viewPos=0;
}
init(){
这个.resizeCanvas(window.innerWidth,window.innerHeight);
常量vsSource=`
属性向量4;
统一mat4 uMVMat;
均匀mat4-jmat;
void main(){
gl_位置=uProjMat*uMVMat*aVertPos;
}
`;
//我的瓷砖在下面的frag着色器中绘制
常数fsSource=`
精密中泵浮子;
均匀vec2-uViewPos;
均匀的vec2 uTileColorSampSize;
均匀采样器2D uTileColorSamp;
void main(){
//瓷砖宽度和高度均为33px,包括1px边框
常量浮动线厚度=(1.0/33.0);
//gridMult组件将为0.0或1.0。这用于放置网格线
vec2 gridMult=vec2(
ceil(最大值(0.0,分形((gl_FragCoord.x-uViewPos.x)/33.0)-线厚度),
ceil(最大值(0.0,分形((gl_FragCoord.y-uViewPos.y)/33.0)-线宽)
);
//tileIndex用于从采样器纹理中提取颜色数据
//由于gl中的像素坐标处于关闭状态,因此添加0.5
vec2 tileIndex=vec2(
地板((gl_FragCoord.x-uViewPos.x)/33.0)+0.5,
地板((gl_FragCoord.y-uViewPos.y)/33.0)+0.5
);
//除以samp大小,因为tex坐标为0.0到1.0
vec4 tileColor=纹理2D(uTileColorSamp,vec2(
tileIndex.x/uTileColorSampSize.x,
tileIndex.y/uTileColorSampSize.y
));
gl_FragColor=vec4(
tileColor.x*gridMult.x*gridMult.y,
tileColor.y*gridMult.x*gridMult.y,
tileColor.z*gridMult.x*gridMult.y,
1.0//我们的采样器中的第四个rgba始终为1.0
);
}
`;
const shader=this.buildShader(vsSource,fsSource);
this.programInfo={
程序:着色器,
属性:{
vertexPosition:this.gl.getAttributeLocation(着色器“aVertPos”)
},
uniformLocs:{
projMat:this.gl.getUniformLocation(着色器'uProjMat'),
MVMat:this.gl.getUniformLocation(着色器“uMVMat”),
viewPos:this.gl.getUniformLocation(着色器“uViewPos”),
tileColorSamp:this.gl.getUniformLocation(着色器'uTileColorSamp'),
tileColorSampSize:this.gl.getUniformLocation(着色器'uTileColorSampSize')
}
};
const buffers=this.initBuffers();
//选中并启用OES_texture_float以允许我们创建采样器tex
如果(!this.gl.getExtension(“OES\u纹理\u浮动”)){
警报(“抱歉,您的浏览器/GPU/驱动程序不支持浮点纹理”);
}
此.gl.clearColor(0.0,0.0,0.15,1.0);
这是总分类的净空深度(1.0);
此.gl.enable(此.gl.DEPTH\u测试);
this.gl.depthFunc(this.gl.LEQUAL);
常数FOV=45*Math.PI/180;//以弧度为单位
const aspect=this.gl.canvas.width/this.gl.canvas.height;
this.projMat=glMatrix.mat4.create();
glMatrix.mat4.透视图(this.projMat,FOV,aspect,0.0100.0);
this.MVMat=glMatrix.mat4.create();
glMatrix.mat4.translate(this.MVMat,this.MVMat,[-0.0,-0.0,-1.0]);
this.gl.bindBuffer(this.gl.ARRAY\u BUFFER,buffers.position);
this.gl.vertexattributepointer(this.programInfo.attribLocs.vertPos,2,this.gl.FLOAT,false,0,0);
this.gl.enableVertexAttributeArray(this.programInfo.attribLocs.vertPos);
这个.glDraw();
}
//glDraw()在上面以及渲染循环的每一帧中调用一次
//(不是
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);