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

时间:2015-04-08 04:23:26

标签: android opengl-es opengl-es-2.0 vuforia opengl-es-lighting

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

我想在原生Android上执行此操作,但我不想使用Unity。

这是几天工作和学习的艰难历程。这是我第一次使用任何类型的OpenGL,Op​​enGL ES 2.0并不能让初学者轻松上手。

所以我的光源位于我立方体顶面的稍上方。我发现如果我在模型空间中计算朗伯因子,我可以得到漫反射效果,无论我的相机如何,一切都保持不变,只有顶面得到任何光。

但是当我转向使用眼睛空间时,它变得很奇怪,而且光似乎跟着我的相机。其他面部变亮,而不仅是顶面。我不明白为什么会这样。为了进行测试,我确保光线位置与预期一样,仅使用距离光源来渲染片段着色器中的像素亮度。因此,我对我的“lightDirectionEyespace”的正确性非常有信心,我唯一的解释是法线的某些东西一定是错的。但我认为我遵循了正确创建正常矩阵的解释......

请帮忙!

然后,当然存在这些漫反射计算是否应该在眼睛空间中进行的问题?如果我只是在模型空间中这样做会有任何缺点吗?我怀疑可能在我后来使用更多模型和灯光并添加镜面和透明度时,它将不再起作用,即使我还没有看到原因。

我的renderFrame方法:(某些变量名称仍然包含“bottle”,这是我在右边的立方体之后要点亮的对象)

private void renderFrame()
{
  ShaderFactory.checkGLError("Check gl errors prior render Frame");

  // Clear color and depth buffer
  GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

  // Get the state from Vuforia and mark the beginning of a rendering section
  final State state=Renderer.getInstance().begin();

  // Explicitly render the Video Background
  Renderer.getInstance().drawVideoBackground();

  GLES20.glEnable(GLES20.GL_DEPTH_TEST);
  GLES20.glEnable(GLES20.GL_BLEND);
  GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);

  // Did we find any trackables this frame?
  if(0 != state.getNumTrackableResults())
  {
    // Get the trackable:
    TrackableResult result=null;
    final int numResults=state.getNumTrackableResults();

    // Browse results searching for the MultiTarget
    for(int j=0; j < numResults; j++)
    {
      result=state.getTrackableResult(j);
      if(result.isOfType(MultiTargetResult.getClassType()))
        break;

      result=null;
    }

    // If it was not found exit
    if(null == result)
    {
      // Clean up and leave
      GLES20.glDisable(GLES20.GL_BLEND);
      GLES20.glDisable(GLES20.GL_DEPTH_TEST);

      Renderer.getInstance().end();
      return;
    }

    final Matrix44F modelViewMatrix_Vuforia=Tool.convertPose2GLMatrix(result.getPose());
    final float[] modelViewMatrix=modelViewMatrix_Vuforia.getData();

    final float[] modelViewProjection=new float[16];
    Matrix.scaleM(modelViewMatrix, 0, CUBE_SCALE_X, CUBE_SCALE_Y, CUBE_SCALE_Z); 
    Matrix.multiplyMM(modelViewProjection, 0, vuforiaAppSession
      .getProjectionMatrix().getData(), 0, modelViewMatrix, 0);

    GLES20.glUseProgram(bottleShaderProgramID);

    // Draw the cube:
    GLES20.glEnable(GLES20.GL_CULL_FACE);
    GLES20.glCullFace(GLES20.GL_BACK);

    GLES20.glVertexAttribPointer(vertexHandleBottle, 3, GLES20.GL_FLOAT, false, 0, cubeObject.getVertices());
    GLES20.glVertexAttribPointer(normalHandleBottle, 3, GLES20.GL_FLOAT, false, 0, cubeObject.getNormals());

    GLES20.glEnableVertexAttribArray(vertexHandleBottle);
    GLES20.glEnableVertexAttribArray(normalHandleBottle);

    // add light position and color
    final float[] lightPositionInModelSpace=new float[] {0.0f, 1.1f, 0.0f, 1.0f};
    GLES20.glUniform4f(lightPositionHandleBottle, lightPositionInModelSpace[0], lightPositionInModelSpace[1],
      lightPositionInModelSpace[2], lightPositionInModelSpace[3]);
    GLES20.glUniform3f(lightColorHandleBottle, 0.9f, 0.9f, 0.9f);

    // create the normalMatrix for lighting calculations
    final float[] normalMatrix=new float[16];
    Matrix.invertM(normalMatrix, 0, modelViewMatrix, 0);
    Matrix.transposeM(normalMatrix, 0, normalMatrix, 0);
    // pass the normalMatrix to the shader
    GLES20.glUniformMatrix4fv(normalMatrixHandleBottle, 1, false, normalMatrix, 0);

    // extract the camera position for lighting calculations (last column of matrix)
    // GLES20.glUniform3f(cameraPositionHandleBottle, normalMatrix[12], normalMatrix[13], normalMatrix[14]);

    // set material properties
    GLES20.glUniform3f(matAmbientHandleBottle, 0.0f, 0.0f, 0.0f);
    GLES20.glUniform3f(matDiffuseHandleBottle, 0.1f, 0.9f, 0.1f);

    // pass the model view matrix to the shader 
    GLES20.glUniformMatrix4fv(modelViewMatrixHandleBottle, 1, false, modelViewMatrix, 0);

    // pass the model view projection matrix to the shader
    // the "transpose" parameter must be "false" according to the spec, anything else is an error
    GLES20.glUniformMatrix4fv(mvpMatrixHandleBottle, 1, false, modelViewProjection, 0);

    GLES20.glDrawElements(GLES20.GL_TRIANGLES,
      cubeObject.getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT, cubeObject.getIndices());

    GLES20.glDisable(GLES20.GL_CULL_FACE);

    // disable the enabled arrays after everything has been rendered
    GLES20.glDisableVertexAttribArray(vertexHandleBottle);
    GLES20.glDisableVertexAttribArray(normalHandleBottle);

    ShaderFactory.checkGLError("MultiTargets renderFrame");
  }

  GLES20.glDisable(GLES20.GL_BLEND);
  GLES20.glDisable(GLES20.GL_DEPTH_TEST);

  Renderer.getInstance().end();
}

我的顶点着色器:

attribute vec4 vertexPosition;
attribute vec3 vertexNormal;

uniform mat4 modelViewProjectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 normalMatrix;

// lighting
uniform vec4 uLightPosition;
uniform vec3 uLightColor;

// material
uniform vec3 uMatAmbient;
uniform vec3 uMatDiffuse;

// pass to fragment shader
varying vec3 vNormalEyespace;
varying vec3 vVertexEyespace;
varying vec4 vLightPositionEyespace;
varying vec3 vNormal;
varying vec4 vVertex;

void main()
{
  // we can just take vec3() of a vec4 and it will take the first 3 entries
  vNormalEyespace = vec3(normalMatrix * vec4(vertexNormal, 1.0));
  vNormal = vertexNormal;
  vVertexEyespace = vec3(modelViewMatrix * vertexPosition);
  vVertex = vertexPosition;

  // light position
  vLightPositionEyespace = modelViewMatrix * uLightPosition;

  gl_Position = modelViewProjectionMatrix * vertexPosition;
}

我的片段着色器:

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);
}

2 个答案:

答案 0 :(得分:2)

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

  1. 来自官方样本(当前Vuforia版本4)的Vuforia CubeObject类具有错误的法线。它们并不都与顶点定义顺序相对应。如果您正在使用示例中的CubeObject,请确保正常定义与面正确对应。 Vuforia失败了......

  2. 如我所料,我的normalMatrix错误地构建了。我们不能只是反转换4x4 modelViewMatrix,我们需要首先从中提取左上方的3x3子矩阵然后反转换它。

  3. 以下是适用于我的代码:

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

    这段代码本身并没有那么有用,因为它依赖于我自己randomly imported from this guy的自定义类Mat3,因为Android和Vuforia似乎都没有提供任何可以反转/转置3x3矩阵的矩阵类。这真的让我质疑我的理智 - 唯一适用于这种基本问题的代码必须依赖于自定义矩阵类?也许我只是做错了,我不知道......

答案 1 :(得分:0)

竖起大拇指没有使用固定功能!我发现你的例子非常有用,可以理解人们还需要将光线转换到眼睛空间中的位置。我发现的所有问题都建议使用glLight

虽然这有助于我解决使用静态光源的问题,如果您希望在保持光源静态(例如旋转对象)的同时对模型进行转换,则代码中缺少某些内容是为了保持跟踪原始模型视图矩阵直到视图被更改,或者直到您正在绘制另一个具有不同模型的对象。如下所示:

vLightPositionEyespace = fixedModelView * uLightPosition;

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

thread on opengl discussion boards有帮助:)