适用于OpenGL ES 2.0的漫反射着色器:光线随相机移动而变化(Android上的Vuforia)

适用于OpenGL ES 2.0的漫反射着色器:光线随相机移动而变化(Android上的Vuforia),android,opengl-es,opengl-es-2.0,vuforia,opengl-es-lighting,Android,Opengl Es,Opengl Es 2.0,Vuforia,Opengl Es Lighting,作为起点,我使用了名为MultiTargets的Vuforia(版本4)示例,该示例跟踪摄影机提要中的三维物理“立方体”,并沿立方体边缘用黄色网格线对其进行增强。 我想要实现的是通过设置自己的灯光位置,移除纹理并在立方体面上使用漫反射照明 我想在原生Android上实现这一点,但我不想使用Unity 经过几天的工作和学习,这是一段艰难的旅程。这是我第一次使用任何类型的OpenGL,OpenGLES2.0对初学者来说都不是那么容易 因此,我有一个光源,位于立方体顶面略上方。我发现,如果我在模型空间

作为起点,我使用了名为MultiTargets的Vuforia(版本4)示例,该示例跟踪摄影机提要中的三维物理“立方体”,并沿立方体边缘用黄色网格线对其进行增强。 我想要实现的是通过设置自己的灯光位置,移除纹理并在立方体面上使用漫反射照明

我想在原生Android上实现这一点,但我不想使用Unity

经过几天的工作和学习,这是一段艰难的旅程。这是我第一次使用任何类型的OpenGL,OpenGLES2.0对初学者来说都不是那么容易

因此,我有一个光源,位于立方体顶面略上方。我发现,如果我在模型空间中计算兰伯特因子,我可以获得正确的漫反射效果,无论我的相机如何,所有东西都保持在原位,只有顶面可以获得任何光线

但是当我开始使用眼睛空间时,它变得很奇怪,光线似乎跟着我的相机。其他面获得灯光,而不仅仅是顶面。我不明白这是为什么。为了进行测试,我已经通过在片段着色器中仅使用到光源的距离渲染像素亮度来确保灯光位置与预期一致。因此,我对我的“LightDirectioneSpace”的正确性相当有信心,我唯一的解释是法线一定有问题。但我认为我遵循了正确创建法线矩阵的解释

救命啊

当然还有一个问题,这些漫射计算是否应该在眼睛空间中进行?如果我只是在模型空间里做,会有什么缺点吗?我怀疑,当我以后使用更多的模型和灯光并添加镜面反射和透明度时,它可能不再起作用,尽管我还不知道为什么

我的renderFrame方法:(一些变量名仍然包含“瓶子”,这是我在获得正确的立方体后下一步要照亮的对象)

和我的片段着色器:

precision highp float; //apparently necessary to force same precision as in vertex shader

//lighting
uniform vec4 uLightPosition;
uniform vec3 uLightColor;

//material
uniform vec3 uMatAmbient;
uniform vec3 uMatDiffuse;

//from vertex shader
varying vec3 vNormalEyespace;
varying vec3 vVertexEyespace;
varying vec4 vLightPositionEyespace;
varying vec3 vNormal;
varying vec4 vVertex;

void main()
{
 vec3 normalModel = normalize(vNormal);
 vec3 normalEyespace = normalize(vNormalEyespace);
 vec3 lightDirectionModel = normalize(uLightPosition.xyz - vVertex.xyz);
 vec3 lightDirectionEyespace = normalize(vLightPositionEyespace.xyz - vVertexEyespace.xyz);

 vec3 ambientTerm = uMatAmbient;
 vec3 diffuseTerm = uMatDiffuse * uLightColor;
 // calculate the lambert factor via cosine law
 float diffuseLambert = max(dot(normalEyespace, lightDirectionEyespace), 0.0);
 // Attenuate the light based on distance.
 float distance = length(vLightPositionEyespace.xyz - vVertexEyespace.xyz);
 float diffuseLambertAttenuated = diffuseLambert * (1.0 / (1.0 + (0.01 * distance * distance)));

 diffuseTerm = diffuseLambertAttenuated * diffuseTerm;

 gl_FragColor = vec4(ambientTerm + diffuseTerm, 1.0);
}

我终于解决了所有的问题。 有两个问题可能会引起未来读者的兴趣

  • 官方示例(当前Vuforia版本4)中的Vuforia CubeObject类具有错误的法线。它们并不都符合顶点定义顺序。如果使用示例中的立方体对象,请确保法线定义与面正确对应。Vuforia失败了

  • 正如人们所怀疑的,我的法线矩阵是错误构建的。我们不能只是反转转置4x4 modelViewMatrix,我们需要首先从中提取左上角的3x3子矩阵,然后反转转置

  • 以下是适用于我的代码:

      final Mat3 normalMatrixCube=new Mat3();
      normalMatrixCube.SetFrom4X4(modelViewMatrix);
      normalMatrixCube.invert();
      normalMatrixCube.transpose();
    

    但是,这段代码本身并没有那么有用,因为它依赖于自定义类Mat3,我认为这是因为Android和Vuforia似乎都不提供任何可以反转/转置3x3矩阵的矩阵类。这真的让我怀疑我的理智——解决这样一个基本问题的唯一代码必须依赖于自定义矩阵类?也许我只是做错了,我不知道…

    为没有在这上面使用固定功能而竖起大拇指!我发现您的示例对于理解还需要将光转换到眼睛空间中的某个位置非常有用。我发现的所有问题都只是建议使用

    虽然这有助于我解决使用静态光源的问题,但如果您希望在保持光源静态(例如旋转对象)的同时对模型进行转换,代码中缺少的一点是跟踪原始modelview矩阵,直到视图发生更改,或者直到绘制另一个具有不同模型的对象。比如:

    vLightPositionEyespace=fixedModelView*uLightPosition

    其中,
    fixedModelView
    可以在renderFrame()方法中更新


    这很有帮助:)

    我认为不需要转换到眼睛空间,您可以直接从标准化向量L和N计算漫反射lambret。L(光向量=lightpos vpos)和N(正常)。浮动扩散系数Lambert=最大值(点(L,N),0.0);我只是将正常矩阵打印到日志中,以检查是否看到任何可疑的东西,我确实注意到了一些东西。在renderFrame()方法的早期,我缩放了我的modelview矩阵:
    matrix.scaleM(modelViewMatrix,0,CUBE\u scale\X,CUBE\u scale\Y,CUBE\u scale\Z)和这些比例因子在60-120范围内。因此,我的所有法线矩阵项都在0.0x范围内。也许我需要使用原始modelView矩阵而不进行缩放来计算法线矩阵?现在就试试。我在上一篇评论中提到的缩放并没有真正改变任何东西,并且仔细想想,这实际上已经不再让我太惊讶了。仍在寻找解决方案。在计算法线矩阵时,我做错了什么?我不知道法线向量的初始值。如果它来自对象(纹理空间?),则它位于与世界或眼睛空间不同的空间。您需要使用TBN矩阵(切线双切线法线)将法线向量转换为光向量使用的空间。您还可以尝试使用Model ViewMatrix将法线转换为眼睛空间。
    
    precision highp float; //apparently necessary to force same precision as in vertex shader
    
    //lighting
    uniform vec4 uLightPosition;
    uniform vec3 uLightColor;
    
    //material
    uniform vec3 uMatAmbient;
    uniform vec3 uMatDiffuse;
    
    //from vertex shader
    varying vec3 vNormalEyespace;
    varying vec3 vVertexEyespace;
    varying vec4 vLightPositionEyespace;
    varying vec3 vNormal;
    varying vec4 vVertex;
    
    void main()
    {
     vec3 normalModel = normalize(vNormal);
     vec3 normalEyespace = normalize(vNormalEyespace);
     vec3 lightDirectionModel = normalize(uLightPosition.xyz - vVertex.xyz);
     vec3 lightDirectionEyespace = normalize(vLightPositionEyespace.xyz - vVertexEyespace.xyz);
    
     vec3 ambientTerm = uMatAmbient;
     vec3 diffuseTerm = uMatDiffuse * uLightColor;
     // calculate the lambert factor via cosine law
     float diffuseLambert = max(dot(normalEyespace, lightDirectionEyespace), 0.0);
     // Attenuate the light based on distance.
     float distance = length(vLightPositionEyespace.xyz - vVertexEyespace.xyz);
     float diffuseLambertAttenuated = diffuseLambert * (1.0 / (1.0 + (0.01 * distance * distance)));
    
     diffuseTerm = diffuseLambertAttenuated * diffuseTerm;
    
     gl_FragColor = vec4(ambientTerm + diffuseTerm, 1.0);
    }
    
      final Mat3 normalMatrixCube=new Mat3();
      normalMatrixCube.SetFrom4X4(modelViewMatrix);
      normalMatrixCube.invert();
      normalMatrixCube.transpose();