我创造了一个房间(4个墙壁,天花板和地板),里面有几个物体,能够像走路一样移动相机.我用各种图像纹理所有表面,一切都按预期工作.
根据具体情况,房间宽14个单位,深16个单位(以原点为中心),高3个单位(原点1个,下面2个).房间中间有两个物体,一个立方体和一个倒金字塔.
然后我去添加一个光源来遮蔽立方体和金字塔.我读过并关注了几个NeHe的端口,所以我把我在课程中工作的内容用于照明并将其应用到我的新代码中.
gl.glEnable(GL10.GL_LIGHTING); gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_AMBIENT,new float[] { 0.1f,0.1f,1f },0); gl.glLightfv(GL10.GL_LIGHT0,GL10.GL_DIFFUSE,new float[] { 1f,1f,GL10.GL_POSITION,new float[] { -4f,0.9f,6f,0); gl.glEnable(GL10.GL_LIGHT0);
结果是立方体和金字塔没有阴影.它们在与光相对的两侧看起来和在面对它时一样.当摄像机直接指向远离光源时,房间看起来就像我添加照明代码之前一样.当我将相机旋转到面向光源时,整个房间(包括物体)会变暗,直到相机直接面向光源时全黑.
这里发生了什么?我阅读了很多关于灯光及其工作原理的文章,但我没有看到任何迹象表明为什么这不会照亮房间的所有侧面,立方体和金字塔根据灯光位置着色.是否有一些预期的光线行为,因为它在房间“内部”?我只是因为我是新手而错过了一些简单的东西吗?
解决方法
@H_502_17@ 3D世界中的每个对象都有一个 normal,它可以帮助OpenGL确定对象需要反射多少光.您可能忘记指定曲面的法线.如果不指定它们,OpenGL将以相同的方式点亮您世界中的所有对象.为了在3D中获得曲面法线,您需要至少三个顶点,这意味着它至少是一个三角形.
样品:
为了计算表面的法线,你需要两个向量.由于3D空间中有三个顶点,这意味着这些采样点可能包含三角形:
// Top triangle,three points in 3D space. vertices = new float[] { -1.0f,1.0f,-1.0f,0.0f,}
鉴于这三点,您现在可以通过以下方式定义两个向量:
// Simple vector class,created by you. Vector3f vector1 = new Vector3f(); Vector3f vector2 = new Vector3f(); vector1.x = vertices[0] - vertices[3]; vector1.y = vertices[1] - vertices[4]; vector1.z = vertices[2] - vertices[5]; vector2.x = vertices[3] - vertices[6]; vector2.y = vertices[4] - vertices[7]; vector2.z = vertices[5] - vertices[8];
现在,当你有两个向量时,最终可以使用Cross Product来获得曲面的法线.不久,十字乘积是一个操作,它产生一个包含垂直于输入向量的角度的新向量.这是我们需要的正常情况.
要在代码中获取交叉产品,您必须编写自己的方法来计算它.理论上,您可以根据此公式计算叉积:
A X B = (A.y * B.z – A.z * B.y,A.z * B.x – A.x * B.z,A.x * B.y – A.y * B.x)
在代码中(通过使用上面的向量):
public Vector3f crossProduct(Vector3f vector1,Vector3f vector2) { Vector3f normalVector = new Vector3f(); // Cross product. The normalVector contains the normal for the // surface,which is perpendicular both to vector1 and vector2. normalVector.x = vector1.y * vector2.z - vector1.z * vector2.y; normalVector.y = vector1.z * vector2.x - vector1.x * vector2.z; normalVector.z = vector1.x * vector2.y - vector1.y * vector2.x; return normalVector; }
在进一步评论之前;您可以在数组中指定法线,并在需要时将它们放入OpenGL中,但是如果您深入研究它并且您的代码将更加灵活,您对此主题的理解会更好.
所以现在我们有一个可以循环的法线,将矢量值分配给普通数组(如NeHe的端口,但动态),并设置OpenGL使用GL_NORMAL_ARRAY,以使OpenGL正确反映对象上的光:
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); // I'm assuming you know how to put it into a FloatBuffer. gl.glNormalPointer(GL10.GL_FLOAT,mNormalsBuffer); // Draw your surface...
另一个最后评论;如果你正在使用其他顶点值(如5.0f,10.0f或更大),你可能想要normalize从crossProduct()方法返回的向量,以获得一些性能.否则,OpenGL必须计算新向量以获得单位向量,这可能是性能问题.
此外,GL_POSITION的新浮点[] {-4f,1f}不太正确.当第四个值设置为1.0f时,意味着灯位置为0,无论前三个值是什么.要为灯光位置指定矢量,请将第四个值更改为0.0f.