2017-03-07 71 views
0

定位燈我有一個關於我的GLES 2.0 Android應用程序的移動點光源一個問題:混淆眼睛和世界空間,而在GLES 2.0

我個人的一個實例走動在一個大的表面。這個人需要在他頭頂上有一道光線才能正確照亮他周圍的一個小區域。由於燈光實例將人物實例作爲其父項,因此其在世界空間中的位置與人物移動的方式完全相同(y-Offset +4)。但是每次啓動應用程序時,光線都不會位於頂部,並且不會完全按照人物移動的方式移動(或者至少看起來不像)。即使光線和人物共享相同的x值和z值,它看起來就像是在人的面前。 人是一個立方體(沒有複雜的模型)。

這裏是我的多維數據集的繪製方法的代碼:

public void draw(float[] pPMatrix, float[] pVMatrix) 
{ 
    float[] MVPMatrix = new float[16]; 
    Matrix.setIdentityM(getParent().getModelMatrix(),0); 
    Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL); 


    Matrix.multiplyMM(MVPMatrix, 0, pVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.multiplyMM(MVPMatrix, 0, pPMatrix, 0, MVPMatrix, 0); 

    // Add program to OpenGL ES environment 
    GLES20.glUseProgram(mProgram); 

    // ..... 

    GLES20.glUniformMatrix4fv(LightingProgram.getMVPMatrixHandle(), 1, false, MVPMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVMatrixHandle(), 1, false, pVMatrix, 0); 

    LightObject lo = mParent.getWorld().getLightObjects().get(0); 
    Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0); 
    GLES20.glUniform3f(LightingProgram.getLightPosHandle(), lo.getLightPosInEyeSpace()[0], lo.getLightPosInEyeSpace()[1], lo.getLightPosInEyeSpace()[2]); 

    // Draw the triangle 
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount); 

} 

的Vertex Shader代碼:

片段着色器代碼:

precision mediump float; 
uniform vec3 u_LightPos; 
uniform sampler2D u_Texture; 

varying vec3 v_Position; 
varying vec4 v_Color; 
varying vec3 v_Normal 
varying vec2 v_TexCoordinate; 

void main() 
{ 

    float distance = length(u_LightPos - v_Position); 

    // Get a lighting direction vector from the light to the vertex. 
    vec3 lightVector = normalize(u_LightPos - v_Position); 

    float diffuse = max(dot(v_Normal, lightVector), 0.0); 
    diffuse = diffuse * (1.0/(1.0 + (0.25 * distance))); 

    // Add ambient lighting 
    diffuse = diffuse + 0.25; 

    gl_FragColor = (diffuse * v_Color * texture2D(u_Texture, v_TexCoordinate)); 
} 

我覺得它有什麼與我在輕物體的位置傳遞的方式有關......但我無法弄清楚什麼是正確的方法。

在此先感謝... :-)

================================= =========

!!編輯!我上傳了一個問題的視頻: https://dl.dropboxusercontent.com/u/17038392/opengl_lighting_test.mp4(2MB)

這個場景中的每個形狀都是一個立方體。當人站在房間中間時,燈光對地板沒有影響。如果該人移動到上角,則光線移動到房間中間。 現在這裏是非常奇怪的事情:因爲光線位於人的上方,所以當人在房間中間時照亮黃色的箱子就好了。如何發現地球可以發生? ;-)

==========================================

編輯2: 好吧,所以我試圖做你說的。但是,作爲一個新秀,我無法做正確:

我拉法的任何多維數據集實例:

public void draw(float[] pPMatrix, float[] pVMatrix) 
{ 
    float[] MVPMatrix = new float[16]; 
    float[] normalVMatrix = new float[16]; 
    float[] normalTransposed = new float[16]; 

    // Move object 
    Matrix.setIdentityM(getParent().getModelMatrix(),0); 
    Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL); 
    Matrix.multiplyMM(MVPMatrix, 0, pVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.multiplyMM(MVPMatrix, 0, pPMatrix, 0, MVPMatrix, 0); 

    // create normal matrix by inverting and transposing the modelmatrix 
    Matrix.invertM(normalVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.transposeM(normalTransposed, 0, normalVMatrix, 0); 

    // Add program to OpenGL ES environment 
    GLES20.glUseProgram(mProgram); 

    // ============================ 
    // POSITION 
    // ============================ 
    getVertexBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getPositionHandle(), COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, getVertexBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getPositionHandle()); 

    // ============================ 
    // COLOR 
    // ============================ 
    getColorBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getColorHandle(), COLOR_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getColorBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getColorHandle()); 

    // ============================ 
    // NORMALS 
    // ============================ 
    // Pass in the normal information 
    if(LightingProgram.getNormalHandle() != -1) 
    { 
     getNormalBuffer().position(0); 
     GLES20.glVertexAttribPointer(LightingProgram.getNormalHandle(), NORMAL_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getNormalBuffer()); 
     GLES20.glEnableVertexAttribArray(LightingProgram.getNormalHandle()); 
     checkGLError("normals"); 
    } 

    // ============================ 
    // TEXTURE 
    // ============================ 
    // Set the active texture unit to texture unit 0. 
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 

    // Bind the texture to this unit. 
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureHandle()); 

    // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. 
    //GLES20.glUniform1i(mTextureUniformHandle, 0); 
    GLES20.glUniform1i(LightingProgram.getTextureUniformHandle(), 0); 


    getTextureBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getTextureCoordinateHandle(), TEXTURE_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getTextureBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getTextureCoordinateHandle()); 

    // Pass the projection and view transformation to the shader 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVPMatrixHandle(), 1, false, MVPMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVMatrixHandle(), 1, false, pVMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getNormalHandle(), 1, false, normalTransposed, 0); 

    LightObject lo = mParent.getWorld().getLightObjects().get(0); 
    Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0); 
    GLES20.glUniform3f(LightingProgram.getLightPosHandle(), lo.getLightPosInEyeSpace()[0], lo.getLightPosInEyeSpace()[1], lo.getLightPosInEyeSpace()[2]); 

    // Draw the triangle 
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount); 

    // Disable vertex array 
    GLES20.glDisableVertexAttribArray(LightingProgram.getPositionHandle()); 
    GLES20.glDisableVertexAttribArray(LightingProgram.getTextureCoordinateHandle()); 
    if(LightingProgram.getNormalHandle() != -1) 
     GLES20.glDisableVertexAttribArray(LightingProgram.getNormalHandle()); 
    GLES20.glDisableVertexAttribArray(LightingProgram.getColorHandle()); 
    checkGLError("end"); 
} 

所以,我更新的頂點着色器的代碼現在是:

uniform mat4 u_MVPMatrix;  // A constant representing the combined model/view/projection matrix. 
uniform mat4 u_MVMatrix;  // A constant representing the combined model/view matrix. 
uniform mat4 u_NMatrix;   // combined normal/view matrix ??? 

attribute vec4 a_Position;  // Per-vertex position information we will pass in. 
attribute vec4 a_Color;   // Per-vertex color information we will pass in. 
attribute vec3 a_Normal;  // Per-vertex normal information we will pass in. 
attribute vec2 a_TexCoordinate; // Per-vertex texture coordinate information we will pass in. 

varying vec3 v_Position;  // This will be passed into the fragment shader. 
varying vec4 v_Color;   // This will be passed into the fragment shader. 
varying vec3 v_Normal;   // This will be passed into the fragment shader. 
varying vec2 v_TexCoordinate; // This will be passed into the fragment shader. 

// The entry point for our vertex shader. 
void main() 
{ 
    // Transform the vertex into eye space. 
    v_Position = vec3(u_MVMatrix * a_Position); 

    // Pass through the color. 
    v_Color = a_Color; 

    // Pass through the texture coordinate. 
    v_TexCoordinate = a_TexCoordinate; 

    // Transform the normal's orientation into eye space. 
    v_Normal = vec3(u_NMatrix * vec4(a_Normal, 0.0)); // THIS does not look right... 
    //v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0)); 

    // gl_Position is a special variable used to store the final position. 
    // Multiply the vertex by the matrix to get the final point in normalized screen coordinates. 
    gl_Position = u_MVPMatrix * a_Position; 
} 

我想,也許如果我倒置並轉置模型矩陣並將其保存到一個正常的矩陣,它可能已經解決了這個問題。 但我認爲我完全錯了。

回答

1

它看起來有點亂。有些代碼缺失,您需要更頻繁地開始評論您的代碼。即使只是針對SO問題。

我不確定畫圖輸入參數是什麼,但我可以假設pPMatrix是投影矩陣,而pVMatrix是視圖矩陣。

然後在代碼中有這個奇怪的行:Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL);,我認爲這個行使人移動到它的當前位置。然後,如果從人的角度來看,我會期望這實際上是視圖矩陣的一部分。在任何情況下,此值都不包含在您用於燈光的組件中。那麼getLightPosInWorldSpace又會返回什麼?

如果我們試圖把它分解一下,你有一個character,它的位置由他的模型矩陣定義。這描述了它在場景中的位置和方向。投影矩陣由您的視圖大小和視野來定義。然後,視圖矩陣根據人的方向或從您在現場觀看的任何位置(lookAt過程最常見)計算得出。

無論您如何定義所有這些,那麼光線位置僅取決於人體模型矩陣。所以你需要將燈光位置(0, 4, 0)乘以角色的模型矩陣。所以這可能是你想在Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0);中做的事情。

通過這樣做,您可以實際測試CPU上燈位置的結果是否正確,取決於字符位置在哪裏。

現在,您需要通過着色器(看到您使用什麼)實際上是MVP矩陣和旁邊的計算光源模型矩陣。 MV矩陣不應該在這裏使用,因爲眼睛的位置不影響你的情況下的照明效果。

現在v_Position必須是在場景座標中計算的片段,因此它必須僅與模型矩陣相乘。這基本上會給出場景中片段(像素)的座標,而不是視圖中的座標。現在使用這個位置來獲取燈光的距離,並繼續計算,就像你已經做的那樣。

然後似乎有你的法線問題。計算法線不是通過將它們與模型矩陣或模型視圖矩陣相乘來完成的。設想一個場景,你有一個正常的(0,1,0),你用一個有翻譯的矩陣相乘(10, 0, 0);結果法線然後是(10, 1, 0)即使歸一化沒有意義,結果必須仍然是(0,1,0),因爲沒有應用旋轉。請看看如何生成矩陣來轉換包含所有可能邊緣情況的法線。但是請注意,您可以使用(模型)矩陣的左上角3x3部分對它們進行轉換,包括大多數情況下的歸一化(對於不應進行歸一化的情況以及未對模型矩陣進行等比例縮放的情況每個軸)。

編輯:

要看看它更理論上你正在處理中,我們通常使用3點矩陣模型,視圖和投影。

投影矩陣定義了屏幕上形狀的投影。在你的情況下,它應該取決於你的視野比率和你想要展示的視野。它不應該影響照明,放置形狀的位置或任何超出所有這些位置都映射到屏幕上的位置。

視圖矩陣通常用於定義您如何查看場景。在你的情況下,你從哪個方向看你的場景。你可能應該使用一個查看程序。這個矩陣不會影響對象的光照或任何位置,也不會影響對象的外觀。

然後,模型矩陣是僅用於在場景中定位特定對象的矩陣。我們之所以使用它,是因爲您可能只有一個頂點緩衝區,用於繪製對象的所有實例。所以在你的情況下,你有3個立方體,它們應該共享相同的頂點緩衝區,但在3個不同的地方繪製,因爲它們的模型矩陣是不同的。

現在你的角色與場景中的任何其他物體都沒有區別。它有一個頂點緩衝區,它有一個模型矩陣。如果您想從您所擁有的第一人稱視角切換到只需將自然基向量與字符模型矩陣相乘,然後在查看方法中使用這些向量來構建新的視圖矩陣。基本載體可能是location(0,0,0),forward(0,0,1),up(0,1,0)。一旦變換這些,你就可以構建「中心」爲location+forward。通過這樣做,您在照明的工作方式或物體照明方式方面仍然沒有任何區別,您對場景的看法應該不會對此產生任何影響。

所以你的燈光附加到一些向量的字符偏移量offset(0,4,0)。這意味着場景中的燈光位置是與您的角色模型矩陣相乘的相同矢量,因爲該矩陣是定義場景中角色位置的矩陣。它甚至可以被解釋爲燈的位置在(0,0,0)處並移動到字符位置,該位置將其與模型矩陣相乘,然後由offset翻譯,再乘以用該矢量創建的平移矩陣。這很重要,因爲例如可以構造這個平移矩陣T和旋轉矩陣R,其圍繞X軸旋轉(例如),然後將它們乘以modelMatrix*T*R將使光圍繞角色旋轉。

因此,假設您擁有所有對象的所有這些矩陣,並且您有明亮的位置,則可以開始查看着色器。您需要構建整個MVP矩陣,因爲它是將對象映射到屏幕上的矩陣。所以這個矩陣僅用於gl_Position。至於場景中的實際像素,您只需將其與模型矩陣相乘即可​​。

然後第一個問題是你還需要改變法線。您需要通過反轉然後轉置模型矩陣來爲它們構建矩陣。 The source。所以用你的法線乘以這個矩陣而不是模型矩陣。

所以現在事情變得很簡單。您已經計算出CPU上的燈光位置並將其作爲制服發送。你有片段現場位置,你有它的正常。因此,通過這些,您可以計算片段着色器中已有的光照。

現在我確實介紹了視圖矩陣對照明沒有任何影響。它確實影響它,但不是在你的情況。您尚未實施任何閃光燈,因此未添加此照明組件。但是,如果(如果)您將添加它,那麼簡單地將您的位置作爲另一個統一然後使用視圖矩陣來獲得相同的結果會更容易。

很難從您發佈的代碼中發現所有問題都存在衝突,但至少看起來光照位置轉換不正確,法線轉換不正確。

編輯:有關調試

當openGL的工作,你需要是發明,當談到調試。看到你的結果還有很多事情可能是錯誤的。在CPU上檢查它們或者記錄一些日誌非常困難,而最好的方法通常是修改着色器的方式,以便您可以獲得結果,從而爲您提供更多信息。

要調試的片段位置的場景:

正如前面提到的在場景中的位置的片段不被你從看的角度來實現。所以它不應該依賴於視圖或投影矩陣。在你的片段着色器中,這是存儲在v_Position中的值。

您需要設置您要測試的邊界,這些取決於您的場景大小(放置牆壁和立方體的位置......)。

你說你的牆被25抵消了,所以假設你的場景在範圍[-30,30]是安全的。您將需要分別調試每個軸,例如,讓我們在-25和綠色值爲25時取一個紅色值。要測試Y座標(通常是高度),您只需使用gl_FragColor = vec4(1.0-(v_Position.y+30)/60, (v_Position.y+30)/60), 0.0, 1.0)即可。這應該在所有對象上顯示一個很好的漸變,其顏色應該是較低的紅色值。然後你對2個其他組件做同樣的事情,對於他們中的每一個,你都應該在每個方向上得到這些好的漸變。漸變應該在整個場景中均勻可見,而不僅僅是每個對象。

調試在現場法線:

法線必須取決於哪種方式,他們所面臨的對象之間是一致的。由於您的案例中的所有對象都與軸線平行並進行標準化,因此這應該非常容易。您預期只有6個可能的值,所以2個通行證應該做這項工作(正面和負面)。

使用gl_FragColor = vec4(max(v_Normal.x, 0.0), max(v_Normal.y, 0.0), max(v_Normal.z, 0.0), 1.0)表示正向法線。這將顯示所有面臨正面X爲紅色的面孔,所有面臨正面Y綠色和所有正面Z藍色。

第二個測試是gl_FragColor = vec4(max(-v_Normal.x, 0.0), max(-v_Normal.y, 0.0), max(-v_Normal.z, 0.0), 1.0),對於那些面對負座標的人來說,確實是一樣的。

要調試的光的位置是:

當第一測試通過的光的位置可通過僅由對象照亮附近進行測試。所以length(u_LightPos - v_Position.xyz)顯示你的光線距離。您必須對其進行規格化,以便在場景中使用highp float scale = 1.0 - length(u_LightPos - v_Position.xyz)/50.0,然後以gl_FragColor = vec4(比例,比例,比例,1.0)的顏色使用此顏色。這將使所有附近的物體變成白色而遠離黑色的物體變成白色。應該顯示一個不錯的漸變。

這些測試點:

你這樣做,因爲你目前的結果取決於在那裏所有的人都可以竊聽多個值。通過隔離問題,您可以輕鬆找到問題所在。

第一個測試放棄了法線和光線位置,所以如果它不正確,它只意味着您將您的位置乘以錯誤的矩陣。在計算v_Position時,您需要確保模型矩陣用於乘以位置。

第二個測試放棄場景中的位置和燈光位置。您仍然可以看到您的場景,但顏色將僅由您的法線定義。如果結果不正確,您可能會有錯誤的法線開始,或者使用正常矩陣時,它們會被錯誤地轉換。您甚至可以禁用正常的矩陣乘法,以查看兩者中的哪一個不正確。如果禁用不能解決問題,那麼法線在頂點緩衝區中是不正確的。

第三個測試放棄了您的法線和您的邏輯來計算照明效果。第一次測試必須通過,因爲我們仍然需要模型的位置。所以如果這個測試失敗了,你很可能不會正確定位燈。其他可能性是法線不正確(您有測試),第三種情況是您錯誤地計算光線效果。

+0

嗨馬蒂奇,非常感謝你看我的代碼。對不起,沒有提供足夠的信息。我沒有想過這個......我將在稍後迴應你的問題/假設。今天一直很忙。 – AudioGuy

+0

遊戲不是第一人稱。這很像暗黑破壞神,所以相機在角色之上。每個立方體都有它的左下角位於(0,0,0)處的模型空間。然後,立方體通過translateM()轉換爲其在世界的最終位置;場景呈現正常。現在,如果立方體在世界上(4,1,3)(因爲它的模型被翻譯成(4,1,3)),我想移動一個光線來說出4,4,3(因爲它總是需要懸停在翻譯的立方體上,所以,如果立方體在(4,1,3)處,我手動更新燈位置爲(4,4,3),這就是getLightPosInWorldSpace()返回時調用的函數。 – AudioGuy

+0

但是在你使用它之前,我看到你正在乘以pVMatrix的位置,爲什麼?然後什麼是getLightPosInWorldSpace? –