ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenGL 끄적임 - Compiling shader
    카테고리 없음 2018. 5. 25. 07:33
    반응형

    OpenGL로 작성한 코드에 대해서 디버깅 방법도 매우 중요함.


    컴파일 시, 컴파일 성공/실패 상태 취득방법

    final int[] compileStatus = new int[1];

    glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0);


    컴파일이 실패했는지 또는 성공했는지 여부를 확인하기 위해 먼저 길이가 1 인 새로운 int 배열을 만들고 compileStatus라고 부릅니다. 그런 다음 glGetShaderiv (shader- ObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0)를 호출합니다. 이것은 OpenGL에게 shaderObjectId와 관련된 컴파일 상태를 읽고 compileStatus의 0 번째 요소에 쓰도록 지시합니다.

    이것은 Android에서 OpenGL을 사용하는 또 다른 일반적인 패턴입니다. 값을 검색하기 위해 길이가 1 인 배열을 사용하고 배열을 OpenGL 호출에 전달합니다. 동일한 호출에서 OpenGL에 결과를 배열의 첫 번째 요소에 저장하도록 지시합니다.


    shader 객체를 OpenGL 객체를 통해 만들면 그 참조는 OpenGL을 통해 일어나며 앞으로이 객체를 참조 할 때마다 OpenGL에 동일한 정수를 다시 전달합니다.

    Compiling shaders

    Frame buffer = Memory block, Android 쪽에서 Display 있게 .


    Resource upload

    Vertex shader 스크린에 vertex들의 final position 계산한다. Fragment OpenGL 그리는 최종 단위인데, Fragment , , 삼각형으로 쪼개는 역할을 한다. Fragment shader 각각 fragment 최종 색상을 결정하는 역할을 한다. 스크린에는 vertex fragment shader 합친 최종 이미지가 화면에 나타난다. 


    program vertex shader fragment shader OpenGL 항상 묶어주는 역할을 한다. 


    glAttachShader(programObjectId, vertexShaderId);

    glAttachShader(programObjectId, fragmentShaderId);

    위의 코드는 우리의 프로그램에 shader들을 붙이는 역할을 한다. 


    shader들이나 program 항상 validation check 해야한다. 객체 id 0일시에는 load fail 것이다.


    glGetProgramiv(pro- gramObjectId, GLES20.GL_LINK_STATUS, linkStatus, 0) 호출하면 program OpenGL 링크되고, 만약에 OpenGL 할말이 있다면 linkStatus 확인해야한다.

    만약에 링크에 실패하면 우리는 우리의 Program 오브젝트를 사용할 없다. 


    마지막으로 우리의 data OpenGL 링크하자. 그리고 우리의 shader 만들어서 OpenGL 링크한다. 

    Program OpenGL 부터 반환받은 id onSurfaceCreated() 선언한다.

    glUseProgram(program);


    이제 Uniform 얻어오자.

    Uniform OpenGL 변수 타입으로 Shader 전달되는 읽기 전용 값을 저장한다. 주로 Matrix, Lighting Parameter, Color 등의 값을 저장하는데 사용된다. 


    precision mediump float;

    uniform vec4 u_Color;

    void main() { 

        gl_FragColor = u_Color;

    }


    gl_FragColor u_Color라고 불리는 uniform assign한다. 우리는 table 그릴 것인데 다양한 색상을 사용할 것이므로 uniform 우리가 그릴 것들의 색상을 set할때 사용한다. 

    http://dalbom.tistory.com/category/OpenGL%20ES


    private static final String U_COLOR = "u_Color";

    private int uColorLocation;

    우리는 Renderer 상수를 하나 만들건데 상수는 우리의 program 안에 uniform location 들고 있을 아이이다. uniform 서로다른 program 에서 같은 이름을 가질수도 있는데 것은 같은 location 가지진 않는다. Program 마다 uniform 다른 location 가진다.


    uColorLocation = glGetUniformLocation(program, U_COLOR);

    onSurfaceCreated () 선언하면 우리 uniform location 받아오게 된다. 


    Attribute location 받아오기 

    uniform 마찬가지로 우리가 사용할 attribute location 사용전에 얻어올 필요가 있다. attribute는 OpenGL이 우리가 그릴 table의 각 position을 저장하는데 사용된다. 우리는 OpenGL attribute 여러개를 자동적으로 asign하도록 있는데, glBindAttribLocation() 호출해서 shader link하기 전에 여러개를 자동적으로 location 얻어올 있다. 방법은 코드 유지보수에도 좋다. 


    private static final String A_POSITION = "a_Position";

    private int aPositionLocation;

    Attribute 상수를 선언해서 shader link 될때 같이 location 받을 있다.


    aPositionLocation = glGetAttribLocation(program, A_POSITION);

    위와 같이 location 얻는다. 우리는 location으로 OpenGL에게 attribute 데이터가 어디있는지 말해줄 있다. 


    다음으로 OpenGL a_Position 데이터가 어디있는지 말해주는 방법이다. onSurfaceCreated() 시점에 아래 코드를 호출한다. 

    vertexData.position(0);

    glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,

        false, 0, vertexData);

    Hockey table 만들기 위한 vertices position들을 담는 floating point values 배열을 만들었다. vertexData라고 native memory buffer 만들었다. 


    OpenGL buffer 부터 position들을 읽어오기 전에, OpenGL 끝이나 중간이 아니고 처음부터 position 읽는 다는 사실에 주목해야한다! buffer internal pointer 가지는데, internal pointer position[0] 부터 읽기 시작한다. glVertexAttribPointer() 호출하면 OpenGL vertexData buffer a_Position 대한 데이터를 찾을 있다. 함수는 매우 중요해서 파라미터를 하나씩 보자.


    Int index : attribute location으로 aPositionLocation = glGetAttribLocation(program, A_POSITION);

    로부터 얻은 location 전달한다. 


    Int size : attribute data count 이다. 우리는 table position 2 demension으로 하기로 결정했었는데, x,y 선언한다면 size 2개이다. 우리는 vertex(1,2) 2개의 components 던질건데, 4개의 components 가지를 vec4 정의된 a_position shader 전달한다. ( 4갠지 모르겠음) table position의 x,y좌표가 선언되어있지 않으면 OpenGL측에서는 처음 3개를 0으로, 마지막 하나를 1로 선언해놓는다.


    Int type : 이는 data type 이다. 우리는 float list 정의했으므로 GL_FLOAT 던진다. 


    boolean normalized : integer data 사용할 선언하며, 우리는 무시하면 된다.


    Int stride : 하나의 배열에 하나 이상의 attribute 저장할때 선언한다. 지금은 하나의 attribute(floating list, a_position) 사용하므로 0 보내서 무시한다. 


    Buffer ptr : OpenGL에게 data 어디서 읽는지 알려준다. buffer 현재 position부터 읽는 다는 점을 알아야한다. vertexData.position(0) 초기화하지 않는다면 마지막 배열을 넘어서 읽기 때문에 crash 있다. 


    여기서 잘못된 파라미터를 전송하면 잘못된 결과나 crash 발생하는데 이와 같은 crash 추적하기가 매우 힘들다. 함수를 호출하게 되면 OpenGL a_position attribute(table position float list) 대해서 데이터를 어디서부터 읽을지 알게 된다. 

     

    마지막으로 vertex array(또 활성화?) 활성화하기 위해서 

          glEnableVertexAttribArray(aPositionLocation);

    glVertexAttribPointer() 다음에 호출한다. 이는 OpenGL 필요로하는 모든 데이터가 어디있는지 있게 된다. 

    이로써 uniform u_Color, attribute a_Position 위치를 얻어올 있었다. OpenGL 변수 이름보다는 location 통해 변수에 접근한다는 것을 알게 되었을 것이다. glVertexAttribPointer() 최종적으로 호출함으로써 OpenGL vertexData로부터 a_Position attribute 대한 데이터를 찾을 있게 한다. 



    이제 스크린에 테이블을 그려보자!

    우리는 최종적으로 OpenGL과 shader들과 변수들의 connection을 완성해봤다. 


    테이블을 그리기 위해서는 다음 코드를 실행해야한다.

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    glUniform4f(uColorLocation, 1.0f, 1.0f, 1.0f, 1.0f);

    glDrawArrays(GL_TRANGLES, 0, 6);


    glUniform4f 를 호출하면 shader에 u_Color 값이 update된다. attribute(table position)과 다르게 uniform들은 default components가 없다. 만약에 vec4로 타입 정의가 되어있으면 4개 다 선언해줘야된다. 우리는 일단 하얀색 table을 그리기 시작할건데 RGBA 순으로 1.0f로 다 선언해줘야된다. 


    color값을 우선 선언해준 뒤에 table을 그리는 걸 선언한다. glDrawArrays()를 호출하는데, 첫번째 파라미터는 삼각형을 그릴 것이라는 의미이다. 삼각형을 그리기 위해서는 삼각형에 대한 최소 3개의 vertices를 전달해야한다. 그래서 두번째 파라미터는 우리 vertex array에서 읽을 시작부분을 의미하고 세번째는 6개의 vertices(x, y)가 있다는 의미이다. 삼각형 한개당 3개의 vertices가 있기 때문에 glDrawArrays()에 6을 줬으므로 삼각형 두개만 그리고 끝난다. 

    float[] tableVerticesWithTriangles = { // Triangle 1

            0f,  0f, // offset 0

            9f, 14f,

            0f, 14f,

    // Triangle 2

            0f,  0f, // offset 3

            9f,  0f,

            9f, 14f,

    // Line 1

            0f,  7f, // offset 6

            9f,  7f,

    // Mallets

    4.5f, 2f, // offset 8

    4.5f, 12f };


    우리는 OpenGL에 선언할 때 POSITION_COMPONENT_COUNT를 2로 주고 있는데 이는 x,y좌표 2개를 의미한다. 

    glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, vertexData);


    그래서 삼각형을 그릴 때 (0, 0), (9, 14), (0, 14)와 (0, 0), (9, 0), (9, 14) 두 개의 삼각형을 그리는 것이다. 


    이제 테이블을 가로지르는 대각선 2개를 그릴 것이다.

    glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);

    glDrawArrays(GL_LINES, 6, 2);


    color 값에 1.0f를 보내고 있는데 이는 빨간색을 의미한다. 그리고 우리의 vertex array에서 6번째부터 읽게되는데, Java array는 0 부터 첫번째이기 때문에 6번째 순서의 의미는 7번째에 있는 좌표를 의미한다. 


    // Line 1

    0f,  7f,

    9f,  7f,


    위 좌표에 따라 GL은 (0, 7)에서 (9, 7)까지 선을 그린다. 


    이제 Mallet용 점을 그려보자.

    // Draw the first mallet blue.

    glUniform4f(uColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);

    glDrawArrays(GL_POINTS, 8, 1);

    // Draw the second mallet red.

    glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 1.0f);

    glDrawArrays(GL_POINTS, 9, 1);


    GL_POINTS을 보내면서 GL에 선을 그리라고 하는 것이다. 첫번째 점은 array offset의 8번째이고 파란색, 두번째 점은 9번째에서 붉은색이다. 

    // Mallets

    4.5f,  2f,

    4.5f, 12f


    (4.5, 2)와 (4.5, 12)의 점 2개를 그린다. 


    우리는 OpenGL이 서로 다른 Device들 스크린에 어떻게 좌표를 알아낼까? 라고 궁금해할 수 있다. 이는 OpenGL이 상대적으로 좌표를 측정함을 알고 있으면 되는데, Device의 화면 크기가 어떻든 [-1, 1] 범위로 스크린 좌표를 가진다고 보면 된다. 


    그래서 우리 table에 그릴 좌표들은 다음과 같이 수정해야한다.


    float[] tableVerticesWithTriangles = { // Triangle 1


        -0.5f, -0.5f,


         0.5f,  0.5f,


        -0.5f,  0.5f,


    // Triangle 2


        -0.5f, -0.5f,


         0.5f, -0.5f,


         0.5f,  0.5f,


    // Line 1


        -0.5f, 0f,


         0.5f, 0f,


    // Mallets


    0f, -0.25f,


    0f, 0.25f };




    마지막으로 Point의 사이즈를 변경하고 싶다면 다음과 같이 vertex_shader에 선언하면 된다. 

    attribute vec4 a_Position;     


    void main()                    

    {                              

        gl_Position = a_Position;

        gl_PointSize = 10.0;

    }


    GL이 그릴 point를 fragment로 분해할 때, gl_PointSize을 중심으로 그리게 된다. 숫자가 크면 클 수록 점은 커진다. 



    반응형
Designed by Tistory.