我已经看过WebGL的几个示例/教程,其中调用了gl.getAttributeLocation()或gl.getUniformLocation()等。但是,这些都在演示/教程场景中。
问题/疑虑是: 例如,gl.getAttributeLocation接受一个字符串作为第二个参数。这是着色器代码中变量的名称。
示例:
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
对我而言,这似乎有点不好,因为如果我碰巧稍微更改着色器代码(如变量名称),那么由于重构我可能会导致各种令人头疼的问题,我必须在更改变量名称的每个地方进行引用。
我希望我能得到我的关注。如果不清楚,请告诉我,我会尝试详细说明。
问题: 我是初学者Opengl-ES / WebGL程序员,所以我知道我必须遗漏一些东西。经验丰富的程序员如何将着色器代码中的变量名与其应用程序代码相关联?
感谢。
答案 0 :(得分:1)
这个想法是以某种方式在一个地方组织所有属性和制服。
我将使用this Three.js demo向您展示他们是如何做到的。这是完整的源代码,我将只展示其中的一部分:
attributes = {
displacement: { type: 'f', value: [] }
};
uniforms = {
amplitude: { type: "f", value: 1.0 },
color: { type: "c", value: new THREE.Color( 0xff2200 ) },
texture: { type: "t", value: THREE.ImageUtils.loadTexture( "img.jpg")
};
因此,开发人员通过指定名称(只编写一次,因此没有您需要关注的名称的多个版本)来设置他想要使用的制服和属性,变量的类型( f-float,c-color,t-texture等...)和变量的值。
所以你在一个地方创建属性/制服,一次,就是这样。引擎还可以自动添加其他几个属性/制服(在这个例子中它是这样做的,对于顶点的顶点和法线的位置等等,但你没有在代码中的任何地方看到(如果有的话,那个行为可以被覆盖)需要))。
引擎会尝试渲染场景,并且会注意到某些网格的着色器没有准备好。所以现在,你只需拥有JavaScript设置变量,但是它可以通过引擎来完成着色器中的所有管理。引擎可能使用您提供的相同变量,但可以使用其他数据扩展它们,例如 attribLocation , uniformLocation ,......但这不应该打扰您的编码。因此,引擎会对所有属性/制服进行逻辑组织输入(如上面的代码所示),并从中生成着色器代码的一部分。
所以变量扩展成如下所示,其中的数据可以直接用于 gl.drawElements , gl.uniform1f 等等:
myMesh.uniforms = {
amplitude: { type: "f", value: 1.0,
shaderLocation: ..., shaderType: "float"},
...
};
myMesh.attributes = {
displacement: { type: 'f', value: [],
shaderLocation: ..., shaderType: "float", bufferedData: ... }
};
然后它会生成其余的着色器代码,然后设置一些标志,表明材质/着色器已准备好使用。如果更改某些属性,则需要手动通知引擎更新,以便可以再次准备着色器。
由于对象的组织方式和动态类型,JavaScript很有用,但OpenGL引擎会使用一些字典,其中包含属性/统一名称作为键,其余重要数据作为字典中的值。< / p>
希望这有帮助。
答案 1 :(得分:0)
我认为你的担忧可能是错位的。您是说如果更改着色器的统一和/或属性名称,则可能需要更改使用它们的代码。这与编程的其他任何部分有何不同?如果我做一个对象
Animal = function(height, weight, numLegs) {
this.height = height;
this.weight = weight;
this.numLegs = numLegs;
}
我有使用该代码的代码
var a = new Animal(1, 150, 4);
console.log(a.numLegs);
然后我改变了动物
Animal = function(height, weight, numLegs) {
this.heightInFeet = height;
this.weightInLbs = weight;
this.numberOfLegs = numLegs;
}
我上面写的代码console.log(a.numLegs);
也必须改变。
换句话说,在GLSL中更改名称以及必须在其他地方更改代码的问题并不是WebGL独有的,实际上是编程中最常见的事情之一。
至于人们如何组织事物,我不知道大多数人做了什么。我已经这样做了:
假设您有一个有效的链接程序,我会遍历位置和制服,并创建一个具有预先制作的setter的对象。然后,我可以将该对象传递给具有与着色器程序匹配的名称/值对的javascript对象。
function createUniformSetters(program) {
function createUniformSetter(info) {
var loc = gl.getUniformLocation(program, info.name);
var type = info.type;
if (type == gl.FLOAT)
return function(v) { gl.uniform1f(loc, v); };
if (type == gl.FLOAT_VEC2)
return function(v) { gl.uniform2fv(loc, v); };
if (type == gl.FLOAT_VEC3)
return function(v) { gl.uniform3fv(loc, v); };
if (type == gl.FLOAT_VEC4)
return function(v) { gl.uniform4fv(loc, v); };
if (type == gl.INT)
return function(v) { gl.uniform1i(loc, v); };
if (type == gl.INT_VEC2)
return function(v) { gl.uniform2iv(loc, v); };
if (type == gl.INT_VEC3)
return function(v) { gl.uniform3iv(loc, v); };
if (type == gl.INT_VEC4)
return function(v) { gl.uniform4iv(loc, v); };
if (type == gl.BOOL)
return function(v) { gl.uniform1i(loc, v); };
if (type == gl.BOOL_VEC2)
return function(v) { gl.uniform2iv(loc, v); };
if (type == gl.BOOL_VEC3)
return function(v) { gl.uniform3iv(loc, v); };
if (type == gl.BOOL_VEC4)
return function(v) { gl.uniform4iv(loc, v); };
if (type == gl.FLOAT_MAT2)
return function(v) { gl.uniformMatrix2fv(loc, false, v); };
if (type == gl.FLOAT_MAT3)
return function(v) { gl.uniformMatrix3fv(loc, false, v); };
if (type == gl.FLOAT_MAT4)
return function(v) { gl.uniformMatrix4fv(loc, false, v); };
if (type == gl.SAMPLER_2D || type == gl.SAMPLER_CUBE) {
return function(unit) {
return function(v) {
gl.uniform1i(loc, unit);
v.bindToUnit(unit);
};
}(textureUnit++);
}
throw ("unknown type: 0x" + type.toString(16));
}
// name to setter object for uniforms
var uniformSetters = {
};
// Look up uniforms
var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
var textureUnit = 0;
for (var ii = 0; ii < numUniforms; ++ii) {
var info = gl.getActiveUniform(program, ii);
if (!info) {
break;
}
var setter = createUniformSetter(info);
uniformSetters[info.name] = setter;
}
return uniformSetters;
}
然后我有一个使用
的功能function applyUniforms(uniformSetters, uniforms) {
for (var name in uniforms) {
var setter = uniformSetters[name];
if (setter) {
setter(uniforms[name]);
}
}
}
然后给出像这样的制服的GLSL程序
uniform vec2 u_texcoordOffset;
uniform vec4 u_color;
uniform float u_multiplier
我可以在运行时执行此操作
// at init time
var uniformSetters = createUniformSetters(someProgram)
var uniforms = {
u_texcoordOffset: [1, 2],
u_color: [1, 0, 0, 1],
u_multiplier: 0.56
};
// -- at draw time --
gl.useProgram(someProgram);
applyUniforms(uniformSetters, uniforms);
我为属性做了类似的事情。
注意:以上只是伪代码。它缺少对数组的支持,如果不支持的话 清晰的纹理已被包裹在某个物体中。 The actual code is here