更新:现在看来大部分内容都已消失。我认为在OpenGL ES 2.0中正确进行照明的方法是编写自己的着色器,以任何方式传递法线,然后在着色器中进行照明。特别是,根据OpenGL ES 2.0文档,不再有glMaterialFv,glLightFv等。(更不用说glNormalPointer和朋友了。)我认为这是为我编译的原因是因为这些东西是由iOS提供的GLKit,虽然显然他们并不是真正的开箱即用替代品。但是,GLKit提供了一些标准着色器来模仿OpenGL ES 1.1中的照明功能。
如果有人有任何进一步的信息,我很感激,但我认为这就是答案。
我是OpenGL的新手,所以提前为任何混淆道歉。我在iOS上使用OpenGL ES 2.0。我在互联网上找到的许多例子似乎已经过时了。例如,glBegin(GL_POLYGON)方法似乎已被弃用,因此我不认为我可以使用glNormal3f,正如大多数旧例子所做的那样。
我从Ray Wenderlich的好tutorial开始,并将其与我建造的游戏集成,成功渲染了一些房间的基本几何形状。现在我只想添加灯光。我不确定什么不起作用,但我怀疑它与法线有关。
我做了一个混乱的尝试来启用GL_LIGHT0,主要来自OpenGL documentation。
//* Some attempts at lighting
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat light0_position[] = { 0.0, 0.0, 7.99, 0.0 };
GLfloat light0_ambience[] = { 0.2, 0.2, 0.2, 1.0 };
GLfloat light0_specular[] = { 1.0, 1.0, 1.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambience);
glLightfv(GL_LIGHT0, GL_SPECULAR, light0_specular);
glShadeModel(GL_SMOOTH);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
//*/
glEnable(GL_DEPTH_TEST);
我使用VBOS将顶点位置和颜色传递给一对简单的着色器。以下内容来自教程:
_positionSlot = glGetAttribLocation(programHandle, "Position");
_colorSlot = glGetAttribLocation(programHandle, "SourceColor");
然后在render:方法中(我修改了顶点结构以包括位置和颜色位置之间的法线)。
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE,
sizeof(Vertex), 0);
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE,
sizeof(Vertex), (GLvoid*) (sizeof(float) * 6));
除了照明外,所有这些都很有效。我计算并重新计算了我的法线。对于平行于X和Y轴的垂直矩形壁,这是微不足道的。它们是GL_ARRAY_BUFFER绑定的缓冲区的一部分,以及位置和颜色信息。
我已经多次尝试使用glNormalPointer:
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, sizeof(Vertex), (GLvoid*)(sizeof(float)*3));
教程中的顶点结构现在是:
typedef struct {
float Position[3];
float Normal[3];
float Color[4];
} Vertex;
上面的调用成功,但没有出现光照效果。一些消息来源建议我也应该使用glVertexAttribPointer作为法线。为此,我修改了教程的顶点着色器:
attribute vec4 Position;
attribute vec4 SourceColor;
attribute vec3 Normal;
varying vec4 DestinationColor;
uniform mat4 Projection;
uniform mat4 Modelview;
void main(void) {
DestinationColor = SourceColor;
gl_Position = Projection * Modelview * Position;
}
并添加了我认为允许我使用Normal变量的调用(虽然我不确定OpenGL如何知道如何处理它):
// just before glLinkProgram()
glBindAttribLocation(programHandle, 1, "Normal");
GLuint _normalSlot = glGetAttribLocation(programHandle, "Normal");
glVertexAttribPointer(_normalSlot, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(float)*3));
我注意到无论我在哪里将Normal变量放在GLSL文件中,glBindAttribLocation总是对该属性失败,并且glGetAttribLocation总是为该属性返回-1。 (我可以使用glBindAttribLocation成功更改SourceColor属性的位置,但它不会接受Normal。)在旧版本的OpenGL / GLSL中曾经有一个gl_Normal变量,但显然不再存在。
我不知所措,只是继续尝试发生在我身上的任何事情。我的下一步将是glInterleavedArrays。有很多活动部件,很难找到问题所在。这可能是我的法线一直都很好,而且光线也存在一些问题。除了照明,有没有办法确认你的法线是否正确?或者,如果法线错了,我应该能从灯光中看到任何东西吗?
对这篇文章的篇幅和混淆表示道歉,但欢迎在这种情况下如何指定法线的任何详细解释。我反复谈到现在OpenGL ES 2.0中的情况有所不同,但是从来没有任何详细的例子。提前谢谢。
答案 0 :(得分:5)
自从发布以来我学到了很多,并且意识到它从来没有得到真正的回答。更新是正确的,但值得解释一下,因为我还没有在任何地方看到过很好的解释。这很简短,可以从一个详细的具体例子中受益,但希望它能够达到主要观点。
您通常在VBO中将法线向量作为顶点属性的三元组进行传输。如果你正在处理纹理,你将在你的代码中定义一个包含最少十二个浮点属性的顶点结构:顶点的三个空间坐标(xyz),顶点处法线向量的三个分量,四个颜色成分(rgba)和那里的纹理坐标的两个组成部分。根据您的照明模型,您可以使用自己定义的更多属性,具体取决于硬件限制,最高可达GL_MAX_VERTEX_ATTRIBS。
这取决于你如何布置结构,因为你是那个将进行所有照明计算的人;这些对OpenGL ES 2.0毫无意义。您必须构建计算每个像素颜色的GLSL顶点和片段着色器程序。您为每个定义的属性向量调用glVertexAttribPointer一次,这会导致属性向量以您指定的名称传递给顶点着色器。您可以使用变量将其从顶点着色器传递到片段着色器。片段着色器中的属性值将通过线性插值确定。
您通常还会传递一些带有更多照明参数的制服。对于从一个顶点到下一个顶点不变的任何东西,请使用制服,例如灯光的位置或强度或光泽等材料属性。将顶点属性用于模型中不同的任何内容。
大部分魔法应该在片段着色器中发生。使用法线向量的最简单的光照效果是漫反射光照,您可以使用这样的片段着色器生成:
varying lowp vec4 FragmentPosition;
varying lowp vec3 FragmentNormal;
varying lowp vec4 FragmentColor;
uniform lowp vec3 LightPosition;
uniform lowp float LightIntensity;
void main() {
// .xyz makes a vec3 out of a vec4
lowp vec3 inLightFrame = FragmentPosition.xyz - LightPosition;
lowp float distance = length(inLightFrame);
// inverse square dependence of intensity
gl_FragColor = max(0.0, -dot(inLightFrame, normalize(FragmentNormal)))
/ distance / distance * LightIntensity * FragmentColor;
}
这假设LightPosition均匀向量在软件和顶点着色器中设置如:
attribute vec4 Position;
attribute vec3 Normal;
attribute vec4 Color;
varying vec4 FragmentPosition;
varying vec4 FragmentNormal;
varying vec4 FragmentColor;
void main() {
FragmentPosition = Position;
FragmentNormal = normalize(Normal);
FragmentColor = Color;
}
此外,还会调用glVertexAttribPointer来定义Position,Normal和Color,例如:
glVertexAttribPointer(glGetAttribLocation(programHandle, "Position"), 3, GL_FLOAT,
GL_FALSE, sizeof(Vertex), (GLvoid*(sizeof(float)*0));
glVertexAttribPointer(glGetAttribLocation(programHandle, "Normal"), 3, GL_FLOAT,
GL_FALSE, sizeof(Vertex), (GLvoid*(sizeof(float)*3));
glVertexAttribPointer(glGetAttribLocation(programHandle, "Color"), 4, GL_FLOAT,
GL_FALSE, sizeof(Vertex), (GLvoid*(sizeof(float)*6));
这假定像
这样的结构typedef struct {
float Position[3];
float Normal[3];
GLubyte Color[4];
} Vertex;
这个简单的例子假设单位强度的白光。您可能需要更多灯光,并且您可能希望为每个灯光指定红色,绿色和蓝色强度。此示例忽略材质属性以及环境光和镜面反射光以及阴影。但这就是你在OpenGL ES 2.0中处理法向量的方法。