2009-12-26 6 views
3

안녕하세요 저는 3D 모델링 앱을 작성 중이며 OpenGL에서 렌더링 속도를 높이고 싶습니다. 현재 내가 사용하는 glBegin/glEnd는 정말 느리고 비추천입니다. 나는 매우 빠른 평면 음영 모델을 그릴 필요가있다. 모든 단일 프레임 CPU에서 법선을 생성합니다. 이것은 매우 느립니다. glDrawElements를 인덱스 된 지오메트리와 함께 사용하려고 시도했지만 법선이 삼각형 수준이 아닌 정점에 지정되어 있기 때문에 정상 생성시 문제가 있습니다.
또 다른 아이디어는 GLSL을 사용하여 기하학 쉐이더에서 GPU의 법선을 생성하는 것이 었습니다. 정상 생성을 위해이 코드를 작성했습니다 :GLSL 셰이더 법선 생성

#version 120 
#extension GL_EXT_geometry_shader4 : enable 

vec3 NormalFromTriangleVertices(vec3 triangleVertices[3]) 
{ 
    // now is same as RedBook (OpenGL Programming Guide) 
    vec3 u = triangleVertices[0] - triangleVertices[1]; 
    vec3 v = triangleVertices[1] - triangleVertices[2]; 
    return cross(v, u); 
} 

void main() 
{ 
    // no change of position 
    // computes normal from input triangle and front color for that triangle 

    vec3 triangleVertices[3]; 
    vec3 computedNormal; 

    vec3 normal, lightDir; 
    vec4 diffuse; 
    float NdotL; 

    vec4 finalColor; 

    for(int i = 0; i < gl_VerticesIn; i += 3) 
    { 
     for (int j = 0; j < 3; j++) 
     { 
      triangleVertices[j] = gl_PositionIn[i + j].xyz; 
     } 
     computedNormal = NormalFromTriangleVertices(triangleVertices); 
     normal = normalize(gl_NormalMatrix * computedNormal); 

     // hardcoded light direction 
     vec4 light = gl_ModelViewMatrix * vec4(0.0, 0.0, 1.0, 0.0); 
     lightDir = normalize(light.xyz); 

     NdotL = max(dot(normal, lightDir), 0.0); 

     // hardcoded 
     diffuse = vec4(0.5, 0.5, 0.9, 1.0); 

     finalColor = NdotL * diffuse; 
     finalColor.a = 1.0; // final color ignores everything, except lighting 

     for (int j = 0; j < 3; j++) 
     { 
      gl_FrontColor = finalColor; 
      gl_Position = gl_PositionIn[i + j]; 
      EmitVertex(); 
     } 
    } 
    EndPrimitive(); 
} 

쉐이더를 응용 프로그램에 통합하면 속도가 향상되지 않습니다. 그것은 이전보다 더 나빴다. 나는 GLSL과 쉐이더 전체에서 초보자이기 때문에 내가 뭘 잘못했는지 모른다. Geforce 9400M이 장착 된 MacBook에서이 코드를 사용해 보았습니다.

- (void)drawAsCommandsWithScale:(Vector3D)scale 
{ 
    float frontDiffuse[4] = { 0.4, 0.4, 0.4, 1 }; 
    CGFloat components[4]; 
    [color getComponents:components]; 
    float backDiffuse[4]; 
    float selectedDiffuse[4] = { 1.0f, 0.0f, 0.0f, 1 }; 

    for (uint i = 0; i < 4; i++) 
     backDiffuse[i] = components[i]; 

    glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse); 
    glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse); 

    Vector3D triangleVertices[3]; 

    float *lastDiffuse = frontDiffuse; 

    BOOL flip = scale.x < 0.0f || scale.y < 0.0f || scale.z < 0.0f; 

    glBegin(GL_TRIANGLES); 

    for (uint i = 0; i < triangles->size(); i++) 
    { 
     if (selectionMode == MeshSelectionModeTriangles) 
     { 
      if (selected->at(i)) 
      { 
       if (lastDiffuse == frontDiffuse) 
       { 
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, selectedDiffuse); 
        lastDiffuse = selectedDiffuse; 
       } 
      } 
      else if (lastDiffuse == selectedDiffuse) 
      { 
       glMaterialfv(GL_BACK, GL_DIFFUSE, backDiffuse); 
       glMaterialfv(GL_FRONT, GL_DIFFUSE, frontDiffuse); 
       lastDiffuse = frontDiffuse; 
      } 
     }  
     Triangle currentTriangle = [self triangleAtIndex:i]; 
     if (flip) 
      currentTriangle = FlipTriangle(currentTriangle); 

     [self getTriangleVertices:triangleVertices fromTriangle:currentTriangle]; 
     for (uint j = 0; j < 3; j++) 
     { 
      for (uint k = 0; k < 3; k++) 
      { 
       triangleVertices[j][k] *= scale[k]; 
      } 
     } 
     Vector3D n = NormalFromTriangleVertices(triangleVertices); 
     n.Normalize(); 
     for (uint j = 0; j < 3; j++) 
     { 
      glNormal3f(n.x, n.y, n.z); 
      glVertex3f(triangleVertices[j].x, triangleVertices[j].y, triangleVertices[j].z);    
     } 
    } 

    glEnd();  
} 

당신이 매우 비효율적하지만 작업입니다 볼 수 있듯이 :

이 내가 대체 할 코드, 더 명확합니다. trianglesvertices 배열에 대한 인덱스 배열입니다.

그리기 위해이 코드를 사용하려고했지만 두 개가 아닌 하나의 인덱스 배열 만 가질 수는 없습니다 (정점 및 법선에 대해 하나씩). 일부 정점이 다른 삼각형에 의해 공유 될 때 이제

glEnableClientState(GL_VERTEX_ARRAY); 

uint *trianglePtr = (uint *)(&(*triangles)[0]); 
float *vertexPtr = (float *)(&(*vertices)[0]); 

glVertexPointer(3, GL_FLOAT, 0, vertexPtr); 
glDrawElements(GL_TRIANGLES, triangles->size() * 3, GL_UNSIGNED_INT, trianglePtr); 
glDisableClientState(GL_VERTEX_ARRAY); 

, 어떻게 그들을 위해 너무 다른 노멀, 노멀 포인터를 지정할 수 있습니다?

+0

glDrawElements와 협력하여 glVertexPointer의 문제점은 무엇입니까? 모든 정점에 대한 법선을 계산할 vertex normal 알고리즘을 사용할 수 있습니다. – pmr

+0

btw 이제 모든 삼각형에 대해 쉐이더가 사용됩니다. 이것은 주 병목이 될 것입니다. 귀하의 그것의 탈출구는 glDrawElements 또는 VBOs입니다. – pmr

+0

까다로운 것은 모든 정점에 대해 일반을 생성하는 것이 아니라 모든 삼각형에 대해 일반을 생성하고 싶다는 것입니다. –

답변

2

결국 렌더링 속도가 빨라졌습니다. 꼭지점이나 삼각형이 변할 때만 CPU의 법선을 다시 계산합니다. 이는 전체 장면이 아닌 하나의 메쉬에서 작업 할 때만 발생합니다. 그것은 내가 원했던 해결책은 아니지만 현실 세계에서는 이전 접근법보다 낫습니다.
전체 지오메트리를 별도의 정점 및 꼭지점 배열에 캐시합니다. 플랫 쉐이딩 (3ds max에서 그룹을 부드럽게하는 것과 비슷한 문제)을 원하기 때문에 인덱스 드로잉을 사용할 수 없습니다.
간단한 glDrawArrays을 사용하고 조명 삼각형 모드에서 삼각형 모드에서 다른 삼각형을 선택하고 선택 취소하고 재료 배열을 선택하지 않기 때문에 (나는 아무 것도 찾지 못했습니다).

1

지오메트리가 변경 될 때만 일반적으로 모든 프레임의 법선을 계산하지 않습니다. 삼각형 당 하나의 법선을 갖기 위해서는 삼각형의 각 꼭지점에 대해 동일한 법선을 설정하면됩니다. 즉, 메쉬의 인접한 삼각형 사이에 꼭지점을 공유 할 수는 없지만 이런 종류의 경우에는 이상한 일이 아닙니다.

+0

나는 그것을 얻을 수 없다. 예를 들어 큐브는 12 개의 삼각형과 8 개의 꼭지점을 가진다. 12 개의 법선과 12 개의 인덱스, 8 개의 꼭지점이 필요합니다. OpenGL을 호출하는 방법을 알지 못했습니다. 내 마음에 오는 유일한 방법은 12 * 3 버텍스를 수행하고, 공유 버텍스를이 버퍼에 복사하고 12 * 3 법선을 수행하고 인덱싱 된 드로잉을 희생하는 것입니다.이 방법은 작동하지만 추악합니다. –

+0

법선이 예를 들어 게임의 모든 프레임을 바꾸지 않는 것은 맞지만 삼각형 메쉬의 정점을 선택하고 크기를 조절하고 회전하는 등의 작업을 할 수있는 앱을 작성하고 있습니다. 따라서 법선은 모든 프레임에서 많이 변경됩니다. 변경된 삼각형 만 업데이트하여 속도를 높일 수는 있지만 특정 상황에서는 도움이되지 않으므로 일반적으로 빠른 속도로 수행하고 싶습니다. –

1

귀하의 질문에이 Normals without Normals 블로그 게시물을 기억합니다.

+0

멋진 기사이지만 내 머리가 너무 많습니다. –