I'd like to get an opinion: I currently have the following program consisting of two multi-colored cubes that can be selected via the one html button and rotated by pressing corresponding keys on the keyboard.
Here is my HTML file:
<html>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform vec3 theta;
uniform vec4 posiz;
void main()
{
// Compute the sines and cosines of theta for each of the three axes in one computation.
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remember: these matrices are column-major
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, -s.z, 0.0, 0.0,
s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
// position matrix
mat4 posMat = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
posiz[0], posiz[1], posiz[2], 1.0 );
// size matrix
mat4 sizMat = mat4( posiz[3], 0.0, 0.0, 0.0,
0.0, posiz[3], 0.0, 0.0,
0.0, 0.0, posiz[3], 0.0,
0.0, 0.0, 0.0, 1.0 );
fColor = vColor;
gl_Position = sizMat * posMat * rz * ry * rx * vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fColor;
void
main()
{
gl_FragColor = fColor;
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/initShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="cubev-1.js"></script>
<body>
<canvas id="gl-canvas" width="512"" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
<br/>
<input type = "button" value = "First" id = "switchButton" ></input>
</body>
</html>
files webgl-utils.js, initShaders.js, and MV.js can be found here:
http://www.cs.unm.edu/~angel/WebGL/7E/Common/
And here is the accompanying javascript file:
var canvas;
var gl;
var numVertices = 36;
var xAxis = 0;
var yAxis = 1;
var zAxis = 2;
var axis1 = 0;
var axis2 = 0;
var rot1 = 1.0; // rate of rotation
var rot2 = 1.0;
var theta1 = [ 0, 0, 0 ];
var theta2 = [ 0, 0, 0 ];
// cube position along x, y, and z axis and size
var posiz1 = [ 0, 0, 0, 1 ];
var posiz2 = [ 0, 0, 0, 1 ];
// used to send info back to html, I think
var thetaLoc;
var posLoc;
var firstCube = true;
var vertices = [
vec3( -0.5, -0.5, 0.5 ),
vec3( -0.5, 0.5, 0.5 ),
vec3( 0.5, 0.5, 0.5 ),
vec3( 0.5, -0.5, 0.5 ),
vec3( -0.5, -0.5, -0.5 ),
vec3( -0.5, 0.5, -0.5 ),
vec3( 0.5, 0.5, -0.5 ),
vec3( 0.5, -0.5, -0.5 )
];
var vertexColors = [
vec4( 0.0, 0.0, 0.0, 1.0 ), // black
vec4( 1.0, 0.0, 0.0, 1.0 ), // red
vec4( 1.0, 1.0, 0.0, 1.0 ), // yellow
vec4( 0.0, 1.0, 0.0, 1.0 ), // green
vec4( 0.0, 0.0, 1.0, 1.0 ), // blue
vec4( 1.0, 0.0, 1.0, 1.0 ), // magenta
vec4( 1.0, 1.0, 1.0, 1.0 ), // white
vec4( 0.0, 1.0, 1.0, 1.0 ) // cyan
];
// indices of the 12 triangles that comprise the cube
var indices = [
1, 0, 3, 3, 2, 1, 2, 3, 7, 7, 6, 2,
3, 0, 4, 4, 7, 3, 6, 5, 1, 1, 2, 6,
4, 5, 6, 6, 7, 4, 5, 4, 0, 0, 1, 5
];
window.onload = function init()
{
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.enable(gl.DEPTH_TEST);
// Load shaders and initialize attribute buffers
var program = initShaders( gl, "vertex-shader", "fragment-shader" );
gl.useProgram( program );
// array element buffer
var iBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
// color array attribute buffer
var cBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(vertexColors), gl.STATIC_DRAW );
var vColor = gl.getAttribLocation( program, "vColor" );
gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vColor );
// vertex array attribute buffer
var vBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer );
gl.bufferData( gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation( program, "vPosition" );
gl.vertexAttribPointer( vPosition, 3, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
// connect location to variable in html
thetaLoc = gl.getUniformLocation(program, "theta");
posizLoc = gl.getUniformLocation(program, "posiz");
//event listeners for buttons
document.getElementById( "switchButton" ).onclick = function ()
{
firstCube = !firstCube; // switch between cubes
if (firstCube) document.getElementById("switchButton").value = "First";
else document.getElementById("switchButton").value = "Second";
};
window.onkeydown = function(event)
{
var key = String.fromCharCode(event.keyCode);
if (firstCube)
{
if (key == 'R') posiz1[0] += .1;
else if (key == 'L') posiz1[0] -= .1;
else if (key == 'U') posiz1[1] += .1;
else if (key == 'D') posiz1[1] -= .1;
else if (key == 'I') posiz1[2] += .1;
else if (key == 'O') posiz1[2] -= .1;
else if (key == 'G') posiz1[3] += .1;
else if (key == 'S') posiz1[3] -= .1;
if (event.shiftKey == 0)
{
if (key == 'X' || key == 'Y' || key == 'Z') rot1 = -1.0;
}
else
{
if (key == 'X' || key == 'Y' || key == 'Z') rot1 = 1.0;
}
if (key == 'X') axis1 = xAxis;
if (key == 'Y') axis1 = yAxis;
if (key == 'Z') axis1 = zAxis;
}
else
{
if (key == 'R') posiz2[0] += .1;
else if (key == 'L') posiz2[0] -= .1;
else if (key == 'U') posiz2[1] += .1;
else if (key == 'D') posiz2[1] -= .1;
else if (key == 'I') posiz2[2] += .1;
else if (key == 'O') posiz2[2] -= .1;
else if (key == 'G') posiz2[3] += .1;
else if (key == 'S') posiz2[3] -= .1;
if (event.shiftKey == 0)
{
if (key == 'X' || key == 'Y' || key == 'Z') rot2 = -1.0;
}
else
{
if (key == 'X' || key == 'Y' || key == 'Z') rot2 = 1.0;
}
if (key == 'X') axis2 = xAxis;
if (key == 'Y') axis2 = yAxis;
if (key == 'Z') axis2 = zAxis;
}
};
render();
}
function render()
{
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// handle rendering of first cube
theta1[axis1] += rot1;
gl.uniform3fv(thetaLoc, theta1);
gl.uniform4fv(posizLoc, posiz1);
gl.drawElements( gl.TRIANGLES, numVertices, gl.UNSIGNED_BYTE, 0 );
// handle rendering of second cube
theta2[axis2] += rot2;
gl.uniform3fv(thetaLoc, theta2);
gl.uniform4fv(posizLoc, posiz2);
gl.drawElements( gl.TRIANGLES, numVertices, gl.UNSIGNED_BYTE, 0 );
requestAnimFrame( render );
}
I have little experience with WebGL and I am trying to get a texture mapped onto the second cube without interfering with the colors on the first. I am trying to go about this by following these instructions:
https://developer.mozilla.org/en-US/docs/Web/WebGL/Using_textures_in_WebGL#The_fragment_shader
which instruct me to update the fragment shader in my html to a value I feel would interfere with normal rendering of the first cube.
Am I perhaps going about it the wrong way? Is it realistically possible to map a texture onto the second cube without interfering with the colors of the first? I'd like an opinion because I don't want to chase what could be a dead end.
By the way, if you would like to run the program yourself, here are the controls:
u - up, d - down, l - left, r - right, i - in, o - out, g - grow, s - shrink
x/y/z - spin along cube's x/y/z-axis
shift + x/y/z - same as above but in opposite direction
and the button switches between the cubes.
Thank you so much for your help and time in advance.
答案 0 :(得分:1)
我认为你可能真的觉得these tutorials很有帮助。特别是this one about matrix math。
特别是,做这么多矩阵创建顶点着色器并不常见。一般来说你所有的代码都是关于rx,ry,rz,posMat&amp; sizeMat通常在JavaScript中完成,只有结果发送到着色器。这使得着色器更加灵活,因为您可以在JavaScript中进行任何矩阵数学运算,而不仅仅是您在着色器中执行的5个硬编码事物。当然,你做这件事的方式也没有技术上的错误。什么对你有用。我只是指出这不是一种常见的做法。
对于纹理与当前着色器的顶点颜色,对于每个具有不同功能的大型程序,通常会有数百甚至数千个着色器。事实上,对于3D引擎或游戏引擎,根据您对特定对象所需的功能生成着色器是非常常见的。
同时针对您的特定情况,而不是2个着色器,另一种方法可能是混合颜色然后将其设置为白色
varying vec4 fColor;
varying vec2 texcoord;
uniform sampler2D texture;
void main(void) {
gl_FragColorl = texture2D(texture, texcoord) * fColor;
}
所以当你想用纹理绘制时
// set attributes for texcoord
gl.bindBuffer(..... texcoordBuffer);
gl.enableVertexAttribArray(texcoordLocation);
gl.vertexAttribPointer(texcoordLocation, ....);
// set the texture
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(...., texture);
// tell the shader which unit you put the texture on
gl.uniform1i(textureLocation, unit);
// turn OFF the attribute for vertex colors
gl.disableVertexAttribArray(fColorLocation);
// set the attribute so it returns white
gl.vertexAttrib4f(fColorLocation, 1, 1, 1, 1);
当你想用顶点颜色绘制时,你可以做相反的
// set attributes for vertex colors
gl.bindBuffer(..... fColorBuffer);
gl.enableVertexAttribArray(fColorLocation);
gl.vertexAttribPointer(fColorLocation, ....);
// turn OFF the attribute for texcoords
gl.disableVertexAttribArray(texcoordLocation);
// bind a white texture
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(...., white1x1PixelTexture);
// tell the shader which unit you put the texture on
gl.uniform1i(textureLocation, unit);
在初始时,您将创建1x1白色像素纹理
var white1x1PixelTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, white1x1PixelTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0,
gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255,255,255,255]));
使用此样式,它还意味着您可以同时使用纹理和顶点颜色,这是为地形图添加多样性的常用方法。只需设置纹理和顶点颜色,它们就会成倍增加。
答案 1 :(得分:-1)
您提出了一个相当高级别的问题,所以这是一个高级答案:
当然可以有一个纹理对象和一个无纹理对象。有两种基本方法可以做到:
使用两个单独的片段着色器,一个用于执行纹理查找,另一个用于您已经使用的顶点颜色。您执行initShaders
两次,并获得两个程序对象。然后使用gl.useProgram
在它们之间切换。
请注意,uniform
s的值是特定于程序的 - 因此,如果您想为两者使用相同的值,则必须为每个程序调用{{1}}一次。
在片段着色器中使用条件并使用统一变量控制它:
gl.uniform...
然后,不是像第一个选项中那样切换程序,而是根据您是否需要纹理,给varying vec4 fColor;
varying vec2 texcoord;
uniform sampler2D texture;
uniform bool useTexture;
void main(void) {
gl_FragColor = useTexture ? texture2D(texture, texcoord) : fColor;
}
一个0或1的值。
在这两个选项中,第一个可以有更好的性能,因为它不需要你为数组添加未使用的属性(纹理时没有颜色,着色时没有纹理坐标)并且不需要GPU来评估条件
第二个使用条件,具有简单的优点。此外,如果您有多个不同的渲染选项,则不需要为所有可能的选项组合编译着色器(这可能非常慢)。
从长远来看,我建议您在工具箱中使用这两种技术并使用最适合该问题的技术。在这种特殊情况下,您只需使用如此少的顶点和如此简单的着色器,以及使用哪一个并不重要。