GPU 렌더링 파이프라인 및 셰이더에 대한 현지어 요약----하나의 기사로 충분합니다.

원본 출처:
https://blog.csdn.net/newchenxf/article/details/119803489
정말 잘 작성되었습니다! 추천

GPU 렌더링 파이프라인 및 셰이더에 대한 현지어 요약----하나의 기사로 충분합니다.

1. 소개

그래픽과 이미지를 만들려면 GPU에 대한 이해가 필요하고,
GPU를 이해하려면 가장 중요한 것은 렌더링 파이프라인(또는 파이프라인)을 이해하는 것이며,
렌더링 파이프라인에서 가장 중요한 부분은 셰이더, 즉 셰이더.

따라서 이 기사에서는 이미지 개발을 위한 입문 튜토리얼로 사용할 수 있는 GPU 렌더링 프로세스 및 셰이더를 요약하려고 합니다.

2 렌더링 파이프라인

파이프라인, 조립 라인이라고도 합니다.

조립 라인이란 무엇입니까?

파이프라이닝이란 작업을 반복적으로 실행할 때 여러 개의 작은 작업으로 세분화하고, 이러한 작은 작업을 겹쳐서 실행함으로써 전체적인 작업 효율성을 향상시키는 것을 의미합니다.

예를 들어
포터를 내리는 경우 세 사람이 차에서 매장으로 이동합니다. 3명은 A, B, C이다.
A는 B에게 주고, B는 C에게 주고, C는 가게에 간다.
A는 다음 이동을 시작하기 전에 C가 상점에 넣을 때까지 기다릴 필요가 없으며, 대신 C는 이동이 진행되는 동안 두 번째 항목 이동을 시작할 수 있습니다.

CPU 처리는 실제로 명령 실행을 명령 가져오기, 디코딩, 데이터 가져오기, 계산 및 결과 쓰기의 다섯 부분으로 나누는 파이프라인 기술을 사용합니다. 그러면 그 과정은 위의 포터와 비슷합니다.

렌더링 파이프라인은 3차원 장면의 정점, 텍스처 및 기타 정보를 2차원 이미지로 변환합니다. 이 작업은 CPU + GPU로 완성됩니다.
렌더링 프로세스는 일반적으로 세 단계로 나뉩니다.
(1) 적용 단계
(2) 형상 단계
(3) 래스터화 단계

应用阶段由CPU完成,几何阶段 和 光栅化阶段 由GPU完成。

물론 파이프라인이기 때문에 세 단계의 실행이 비동기식이라는 뜻이다. 즉, CPU 실행이 완료된 후 다음 호출을 시작하기 전에 GPU가 실행을 완료할 필요가 없습니다.
또한 GPU가 실행하는 지오메트리 단계와 래스터화 단계도 하위 작업으로 세분화되는데, 이 역시 파이프라인 기술을 사용합니다.

2.1 CPU와 GPU는 어떻게 병렬로 작동하나요?

대답은 '예'입니다 命令缓冲区. 명령 버퍼에는 명령 대기열이 포함되어 있습니다. CPU 추가 명령, GPU 읽기 명령.
CPU가 일부 개체를 렌더링해야 할 때 명령 버퍼에 명령을 추가하고, CPU가 렌더링 작업을 완료하면 계속해서 버퍼에서 명령을 꺼내 실행할 수 있습니다.
여기에 이미지 설명을 삽입하세요.
녹색 [렌더링 모델]이 우리가 말하는 것입니다 Draw Call.
OpenGL의 예를 들어보세요:

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

주황색 [렌더링 상태 변경]은 데이터 로드, 셰이더 변경, 텍스처 전환 등을 위한 것입니다. OpenGL의 예를 들면 다음과 같습니다.

GLES20.glUseProgram(program);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);

이미지 프레임을 생성할 때 객체 모델이 여러 개인 경우 DrawCall이 하나씩 제출됩니다. GPU는 컬러 버퍼에 하나씩 그린 다음 이를 혼합합니다. GPU가 DrawCall을 하나씩 처리하지만 GPU에도 내부 파이프라인이 있으므로 B를 그리기 전에 A가 그리기를 마칠 때까지 기다릴 필요가 없습니다. A가 첫 번째 작은 단계를 완료하면 두 번째 단계로 진입하게 되며, B는 아래에서 언급하는 정점 셰이더인 첫 번째 단계를 시작할 수 있습니다.

프레임을 그린 후 swapBuffer가 호출되어 색상 버퍼의 데이터를 화면에 제출하여 표시합니다.

3 CPU 처리----응용 단계

주요 작업:
(1) 비디오 메모리(GPU 메모리)에 데이터 로드
(2) 렌더링 상태 설정
(3) Call Draw Call

3.1 비디오 메모리에 데이터 로드

GPU는 일반적으로 메모리에 직접 접근할 수 없기 때문에 비디오 메모리(그래픽 카드 메모리)라는 메모리를 직접 만들었습니다. CPU는 렌더링에 필요한 일부 데이터를 하드 디스크나 네트워크에서 메모리로 로드한 다음 이를 비디오 메모리로 보냅니다.
데이터에는 모델 데이터(정점 좌표 + 텍스처 좌표)와 텍스처 이미지라는 두 가지 주요 유형이 있습니다. 법선 방향과 같은 다른 데이터의 양은 매우 적습니다. 비디오 메모리에 로딩한 후 CPU 메모리에 있던 데이터를 삭제할 수 있는데, 예를 들어 비트맵을 이용해 텍스처를 생성하고 GPU에 로딩한 후 재활용할 수 있다.

3.1.1 모델 데이터 로딩 예

다음은 Android 비디오 재생 및 렌더링의 예를 사용하여 모델 데이터를 로드하는 방법을 보여줍니다.

영상은 2차원 직사각형 프레임 안에 표시되므로 꼭지점 좌표 4개와 이에 대응하는 텍스처 좌표 4개만 필요합니다. 텍스처는 디코딩된 비디오 이미지를 사용합니다.

    //顶点数据
    private float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f
    };
    //纹理坐标数据
    private float[] fragmentData = {
            0f, 0f,
            1f, 0,
            0f, 1f,
            1f, 1f
    };

	public void onCreate() {
		//顶点坐标数据在JVM的内存,复制到系统内存
        vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(vertexData);
        vertexBuffer.position(0);
        //纹理坐标数据在JVM的内存,复制到系统内存
        fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(fragmentData);

		//VBO全名顶点缓冲对象(Vertex Buffer Object),他主要的作用就是可以一次性的发送一大批顶点数据到显卡上
 		int [] vbos = new int[1];
        GLES20.glGenBuffers(1, vbos, 0);
        vboId = vbos[0];


		//下面四句代码,把内存的数据复制到显存中
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20. GL_STATIC_DRAW);
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
	}

코드에 이미 주석이 달렸지만 여기서도 설명할 수 있습니다.

결국 vertexData
는 Java에서 정의한 변수로 시스템 메모리가 아닌 JAVA 가상 머신의 메모리를 사용하므로 GPU에 직접 줄 수는 없습니다. 따라서 먼저 ByteBuffer를 사용하여 vertexData를 시스템 메모리에 복사하세요.

다음으로 버텍스 버퍼 객체(Vertex Buffer
Object)를 생성하는데, 그 주요 기능은 대량의 버텍스 데이터를 한번에 그래픽 카드로 보내는 것입니다. 그런 다음 glBufferData를 호출하여 데이터를 그래픽 카드 메모리에 복사하세요! 이런 식으로 나중에 GPU가 작동하기 시작하면 빠르게 액세스할 수 있습니다.

3.1.2 텍스처 로딩 예시

다음은 텍스처 이미지를 로드하는 Android 앱의 또 다른 예입니다.

    public static int createTexture(Bitmap bitmap){
        int[] texture=new int[1];

        //生成纹理
        GLES20.glGenTextures(1,texture,0);
        //生成纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,texture[0]);
        //设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST);
        //设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
        //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
        //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);

        if(bitmap!=null&&!bitmap.isRecycled()){
            //根据以上指定的参数,生成一个2D纹理,上传到GPU
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        }
        return texture[0];
    }

먼저 glGenTextures를 사용하여 텍스처 ID를 생성합니다. 현재로서는 아직 실제 데이터가 생성되지 않았습니다.
다음으로 텍스처 속성을 설정한 다음 texImage2D를 호출합니다. 그러면 먼저 텍스처 버퍼가 생성되고 비트맵이 버퍼에 복사됩니다. 그러면 끝났습니다. 위 함수가 종료되면 비트맵의 내용이 GPU에 복사되고 필요에 따라 재활용될 수 있습니다.

강조해야 할 점은 不管是加载顶点还是纹理,都只需要在初始化时,一次性完成Draw에서 매번 로드할 필요가 없다는 것입니다. 그렇지 않으면 의미가 없습니다.

3.2 렌더링 상태 설정

즉, 어떤 버텍스 셰이더/프래그먼트
셰이더를 사용할지, 광원 속성, 재질(텍스처) 등 장면의 메시(모델)가 어떻게 렌더링되는지 선언합니다. 서로 다른 메쉬를 그릴 때 렌더링 상태를 전환하지 않으면 전후의 메쉬는 동일한 렌더링 상태를 사용하게 됩니다.

3.3 드로우콜 콜

앞서 DrawCall에 대해 언급한 적이 있는데, 실제로는 명령이라는 것을 이미 알고 계실 것입니다.시작자는 CPU이고 수신자는 GPU입니다.
Draw Call이 호출되면 GPU는 렌더링 상태(재료, 텍스처, 셰이더) 및 모든 정점 데이터를 기반으로 계산을 수행하고 GPU 파이프라인이 실행되기 시작합니다.

다시 한번 강조하고,

3.4 요약

첫 번째 단계에서는 로드가 하나만 필요하므로 그릴 때마다 로드할 필요가 없습니다. 두 번째와 세 번째 항목은 Draw를 할 때마다 설정해야 합니다.
여기서는 예를 들어 비디오나 그림을 그리는 데 onDraw 함수를 사용합니다.

 public void onDraw(int textureId)
    {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f,0f, 0f, 1f);
        //设置渲染状态: program根据具体的shader编译,这里就是指定用哪个shader
        GLES20.glUseProgram(program);
        //设置渲染状态:指定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        //设置渲染状态:绑定VBO
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);

        //设置渲染状态:指定顶点坐标在VBO的从0开始,8个数据
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8,
                0);

        //设置渲染状态:指定顶点坐标在VBO的从8*4(float是4个字节)开始,8个数据
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8,
                vertexData.length * 4);

        //调用Draw Call, GPU 管线开始工作
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        //绘制完成,恢复现场
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
    }

4 GPU 처리 ---- 기하학 단계

기하학 단계와 래스터화 단계를 함께 놓고 그림을 그릴 수 있습니다.
여기에 이미지 설명을 삽입하세요.
물론, 위의 그림이 완전하지 않을 수도 있고 중간에 선택적인 단계가 있을 수도 있으니 가장 중요한 것만 나열했습니다.
그 중 파란색 부분의 버텍스 셰이더와 프래그먼트 셰이더는 개발자가 완전히 사용자 정의할 수 있으며 대부분의 이미지 개발 학생들이 주의해야 할 두 단계이기도 합니다.

4.1 정점 데이터란 무엇입니까?

기본적인 설명부터 시작해 보겠습니다. GPU가 이해하는 데이터는 점, 선, 삼각형뿐입니다.
이는 꼭지점 1개, 꼭지점 2개, 꼭지점 3개에 해당합니다. 이 세 가지를 이라고도 합니다 图元. 점과 선은 일반적으로 2D 장면에서 사용되며, 3D 장면에서는 기본적으로 객체 모델에 접합된 N개의 삼각형으로 구성됩니다.

아래 그림은 캐릭터 모델의 예입니다.
여기에 이미지 설명을 삽입하세요.
일부 세부 사항을 확대하면 모두 삼각형으로 구성되어 있음을 알 수 있습니다. 삼각형의 꼭지점은 우리가 꼭지점 좌표라고 부르는 것입니다.
여기에 이미지 설명을 삽입하세요.
모든 삼각형을 2차원 이미지 텍스처로 붙여넣으면 완전한 이미지가 됩니다
여기에 이미지 설명을 삽입하세요.
.顶点数据,一般会包含顶点坐标 + 纹理坐标。

모델 파일에는 일반적으로 정점 좌표, 텍스처 좌표, 텍스처 자체(여러 텍스처가 있을 수 있음) 등이 포함됩니다. 예를 들어 3D max라는 접미사가 obj인 모델 파일을 열면 다음과 같은 텍스트 내용을 볼 수 있습니다.

여기에 이미지 설명을 삽입하세요.

4.2 버텍스 셰이더

버텍스 셰이더는 GPU 내부 파이프라인의 첫 번째 단계입니다.

처리 단위는 정점, 즉 입니다 每个顶点,都会调用一次顶点着色器.
주요 작업은 좌표 변환, 정점별 조명, 후속 단계(예: 프래그먼트 셰이더)를 위한 데이터 준비입니다.

坐标转换,即把顶点坐标从模型坐标空间转换到齐次裁切坐标空间。

Unity 셰이더 코드는 다음과 같습니다. Unity는 버텍스 셰이더와 프래그먼트 셰이더 코드를 파일에 넣지만, Unity 엔진은 파일을 구문 분석하고 두 셰이더 코드 세그먼트를 변환한 후 GPU에 전달합니다.

Shader "Unlit/SimpleUnlitTexturedShader"
{
    Properties
    {
        // 我们已删除对纹理平铺/偏移的支持,
        // 因此请让它们不要显示在材质检视面板中
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            // 使用 "vert" 函数作为顶点着色器
            #pragma vertex vert
            // 使用 "frag" 函数作为像素(片元)着色器
            #pragma fragment frag

            // 顶点着色器输入
            struct appdata
            {
                float4 vertex : POSITION; // 顶点位置
                float2 uv : TEXCOORD0; // 纹理坐标
            };

            // 顶点着色器输出("顶点到片元")
            struct v2f
            {
                float2 uv : TEXCOORD0; // 纹理坐标
                float4 vertex : SV_POSITION; // 裁剪空间位置
            };

            // 顶点着色器
            v2f vert (appdata v)
            {
                v2f o;
                // 将位置转换为裁剪空间
                //(乘以模型*视图*投影矩阵)
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                // 仅传递纹理坐标
                o.uv = v.uv;
                return o;
            }
            
            // 我们将进行采样的纹理
            sampler2D _MainTex;

            // 像素着色器;返回低精度("fixed4" 类型)
            // 颜色("SV_Target" 语义)
            fixed4 frag (v2f i) : SV_Target
            {
                // 对纹理进行采样并将其返回
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

코드에 명확하게 주석이 달렸습니다.
Appdata는 정점 좌표와 텍스처 좌표를 포함하는 정점 셰이더의 입력입니다.
일반적으로 정점 셰이더는 정점 좌표만 처리하고 공간 변환을 수행합니다. 텍스처 좌표는 일반적으로 프래그먼트 셰이더로 전송됩니다.

vert 함수는 정점 셰이더의 코드입니다. Unity 엔진은 이를 다음과 유사한 주요 기능을 사용하여 GLSL 코드로 변환한 후 컴파일합니다.

attribute vec4 v_Position;
attribute vec4 f_Position;
uniform mat4 uMVPMatrix;
varying vec2 textureCoordinate;

void main()
{
    gl_Position = uMVPMatrix* v_Position;
    textureCoordinate = f_Position.xy;
}

공간 변환 코드는 매우 간단합니다. 좌표에 MVP 행렬을 곱하면 됩니다!

MVP矩阵다음으로 를 기반으로 한 공간변형의 개념을 확장 해 본다 .

시작하기 전에 먼저 강조하겠습니다.main函数会执行几次?
答案是,有几个顶点,就执行几次,且是并行的!

4.3 좌표 공간

좌표 공간이란 무엇입니까? 사실 그는 우리 삶의 모든 곳에 존재합니다. 예를 들어 당신과 당신의 친구가 박물관 정문에서 100m 떨어진 곳에서 만나기로 합의했다면, 이때 당신이 말하는 장소는 박물관 정문을 원점으로 하는 공간이다. 따라서 좌표 공간은 상대적인 개념입니다.

게임의 세계에서도 마찬가지다.

다양한 참조 개체에 따라 다음과 같이 나눌 수 있습니다.模型空间-世界空间-观察空间-裁剪空间-屏幕空间。

3D 세계에서 정점 좌표에는 (x, y, z)라는 세 가지 데이터가 있지만 변환/회전/크기 조정 및 기타 변환을 용이하게 하려면 w 구성 요소를 추가해야 합니다. 즉, (x, y, z,
w), 이 4차원 좌표를 동차좌표라 한다. 이에 대한 지식은 섹션 5의 부록 지식을 참조하십시오 .

다음으로, 이 공간들을 하나씩 논의해보자.

4.3.1 모델 공간

다양한 소프트웨어에서 적용할 수 있는 3D Max에서 캐릭터 모델을 생성합니다.
모델 내부의 경우 좌표 원점(예: 심장)이 있고 신체의 각 부분은 심장을 기준으로 한 좌표 값을 가지며
각 모델 객체는 자체적인 독립적인 좌표 공간을 갖습니다. 그래서 객체 공간(Object Space) 또는 로컬 공간(Local Space) 이라고도 부를 수 있습니다 .

4.3.2 월드 공간

게임 내 지도는 작은 세계로 활용될 수 있으며, 지도의 중심은 좌표 원점으로 활용될 수 있습니다. 캐릭터 모델 전체는 지도에 배치되었을 때 지도 중심을 기준으로 한 좌표 값을 갖습니다. 이곳은 월드 공간입니다.

모델 공간의 좌표를 월드 공간의 좌표로 변환하는 것은 행렬 연산을 통해 얻을 수 있습니다. 이 행렬을 모델 행렬 이라고 합니다 .
실제로 모델을 지도에 배치할 때 먼저 크기를 조정한 다음 회전하거나 변환할 수 있습니다. 이 세 가지에는 대응하는 행렬이 있습니다. 자세한 내용은 부록을 참조하세요 .

즉, 이 행렬은 지도에 있는 객체의 크기 조정, 회전 및 이동 매개변수에 의해 결정됩니다.
변환의 목적은 지도에서 객체의 특정 꼭지점의 특정 위치를 얻는 것입니다.

4.3.3 관측공간

라고 할 수도 있습니다 摄像机空间
. 관찰 공간이란 게임 맵이 너무 커서 콘텐츠의 일부만 볼 수 있다는 의미입니다. 따라서 게임 맵에서 역시 월드 공간에 있는 카메라를 정의합니다. 카메라는 우리의 눈에 해당하며, 카메라가 비추는 곳마다 우리가 보는 사진이 바로 그것입니다.

3D 게임에서는 카메라와 사용자 캐릭터가 서로 연결되는 경우가 많습니다. 캐릭터 객체가 가는 곳마다 카메라 객체가 움직이며, 어디를 가든 풍경을 볼 수 있는 효과가 있습니다!

따라서 지도의 모든 객체는 카메라가 좌표 원점으로 사용되는 경우 카메라를 기준으로 한 좌표 값도 갖습니다. 카메라를 원점으로 하는 공간이 관찰공간이다.

월드 공간의 좌표를 관찰 공간으로 변환하는 것도 행렬 연산을 통해 얻을 수 있습니다. 이 행렬을 뷰 행렬(View Matrix) 이라고 합니다 .

이 행렬은 어떤 매개변수에 의존합니까?

우선, 카메라 자체도 월드 좌표계에 위치하는데, 카메라가 어디를 보고 있는지 고려하지 않는다면 실제로는 매우 간단한 변환 행렬입니다.
즉, 카메라 A의 좌표가 (-3, 0, 0)이라면 카메라는 월드 원점 O를 기준으로
(-3, 0, 0) 이동한 결과 입니다. 카메라 좌표가 새 원점으로 사용되면 원래 세계 원점 O는 카메라에서 (3, 0, 0) 이동한 결과와 동일합니다. 따라서 두 좌표계의 변환행렬은 거의 병진행렬이다.

물론 실제 카메라 방향은 눈이 정면을 보고 있는지 아니면 거꾸로 보고 있는지에 따라 달라집니다. 거꾸로 보는 것이 무엇인지 모르시나요? 아래 그림을보세요
여기에 이미지 설명을 삽입하세요.

따라서 이 행렬은 항상 세 가지 요인의 영향을 받습니다.
복잡한 계산 과정을 잊어버리고 고민을 해결해주는 함수입니다.

glm::mat4 CameraMatrix = glm::LookAt(
    cameraPosition, // the position of your camera, in world space
    cameraTarget,   // where you want to look at, in world space
    upVector        // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too
);

4.3.4 클리핑 공간

클리핑 공간은 카메라가 볼 수 있는 영역입니다. 이 영역은 광학 절두체 가 됩니다 . 이는 클리핑 평면이
되는 6개의 평면으로 구성됩니다 . 두 가지 투영 방법을 포함하는 두 가지 유형의 광학 절두체가 있습니다 . 하나는 원근 투영 이고 다른 하나는 직교 투영 입니다 .

이 공간에 완전히 위치한 프리미티브는 유지되고,
완전히 공간 외부에 있는 프리미티브는 완전히 삭제되며,
공간 경계와 교차하는 프리미티브는 잘립니다.

아래 그림은 투시 투영의 개략도입니다. 캐릭터는 완전히 시각적 원뿔 안에 있으므로 오른쪽 하단에 있는 작은 그림은 최종 사용자가 보는 2D 그림입니다.
여기에 이미지 설명을 삽입하세요.
물론, 사진 속 Near와 Far에 대해서는 표시의 편의를 위해 상대적으로 작은 값을 설정했습니다. 실제 게임에서는 Near와 Far가 매우 다릅니다. 예를 들어 Near = 0.1, Far =
1000입니다. 이는 인간의 눈이 볼 수 있는 범위와 유사하며, 눈에 가까운 것은 볼 수 없고, 아주 먼 것은 볼 수 없습니다.

그렇다면 광학 원뿔 내에 무언가가 있는지 확인하는 방법은 무엇입니까?
대답은 투영 행렬을 통해 정점을 클리핑 공간으로 변환하는 것입니다.
이 매트릭스는 기본적으로 카메라 매개변수에 의해 결정됩니다.

매개변수 다이어그램은 다음과 같습니다:
여기에 이미지 설명을 삽입하세요.

FOV는 시야(Field of View)를 나타내며
Near와 Far는 카메라의 원점과 클리핑 평면으로부터의 거리를 나타내며
AspectRatio는 클리핑 평면의 종횡비를 나타냅니다.

관측 공간에서 클리핑 공간까지의 변화 행렬이 투영 행렬이 됩니다 .

행렬은 다음 함수를 사용하여 생성할 수 있습니다.

// Generates a really hard-to-read matrix, but a normal, standard 4x4 matrix nonetheless
glm::mat4 projectionMatrix = glm::perspective(
    glm::radians(FoV), // The vertical Field of View, in radians: the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in)
    AspectRatio,       // Aspect Ratio. Depends on the size of your window. such as 4/3 == 800/600 == 1280/960, sounds familiar
    Near,              // Near clipping plane. Keep as big as possible, or you'll get precision issues.
    Far             // Far clipping plane. Keep as little as possible.
);

구체적인 수식 생성 원리는 여기에서 확장되지 않습니다. 자세한 내용은 Google에 문의하거나 여기(https://zhuanlan.zhihu.com/p/104768669)에서 확인할 수 있습니다.

투영 행렬에는 투영이라는 단어가 있지만 실제 투영을 수행하지는 않습니다. 대신에 그는 프로젝션을 준비하고 있었습니다. 프로젝션은 다음 단계인 화면 공간으로 변환될 때까지 사용되지 않습니다.

투영 행렬 연산이 수행된 후, 즉 클리핑 공간에 도달한 후 w 값이 반드시 0과 1일 필요는 없으며 특별한 의미를 갖습니다. 특히 정점이 시각적 절두체에 있는 경우 변환된 좌표는 다음을 충족해야 합니다.

-w <= x <= w
-w <= y <= w
-w <= z <= w

요구 사항을 충족하지 못하는 항목은 제거되거나 축소됩니다.

예를 들어,
관찰 공간에 대한 캐릭터 모델의 손 정점 좌표는
[9, 8.81, -27.31, 1] 이고
, 클리핑 공간으로 변환한 후의 값은
[11.691, 15.311, 23.69, 27.3] 입니다.
.그러면 정점이 클립 공간에 있고 표시될 수 있습니다.

이전 설명은 원근 투영을 기반으로 했습니다. 그렇다면 직교 투영과 원근 투영의 차이점은 무엇입니까?
透视投影:越远的东西,呈现在屏幕的越小; 正交投影,远和近的同样大小东西,呈现在屏幕上一样大.

여기 손으로 그린 ​​그림이 있는데 무슨 말인지 아실 거예요!
여기에 이미지 설명을 삽입하세요.

위에서 볼 수 있듯이 원근 투영은 3D 효과가 있는 반면 직교 투명도는 그렇지 않아 2D 게임에 더 적합합니다.

4.3.5 화면 공간

스크린 공간은 2차원 공간이다. 이곳은 우리가 보게 될 사진이기도 하고, 우리가 알아야 할 마지막 공간이기도 합니다. (긴 숨을 쉬셨죠, 하하)

정점을 클리핑 공간에서 화면 공간으로 변환하려면 3차원이 2차원이 되므로 이를 이라고 합니다 投影.

이 투영은 주로 두 가지 작업을 수행합니다.

첫째, 동질적인 분할이 필요하다 . 복잡하다고 생각하지 마세요. 실제로는 w 구성 요소를 사용하여 x, y, z 구성 요소를 찾는 것뿐입니다. Open GL에서는 이 단계에서 얻은 좌표를 归一化的设备坐标(Normalized Device Coordinates, NDC)라고 합니다.
NDC의 x, y, z의 유효한 값 범위는 모두 입니다 负1到1. 이 값을 벗어나는 점은 제거됩니다.

아래 그림은 NDC의 개략도이다. 길이, 너비, 높이가 [-1,1] 범위에 있는 정육면체처럼 보입니다.
여기에 이미지 설명을 삽입하세요.
NDC 존재의 의미, 두 번째 단계.

둘째, 화면 좌표를 얻으려면 화면에 매핑되어야 합니다. OpenGL에서 화면 왼쪽 하단의 픽셀 좌표는 (0, 0)이고 오른쪽 상단은 (pixelWidth, pixelHeight)입니다.
예를 들어 정규화된 좌표가 (x, y)이고 화면 너비와 높이가 (w, h)인 경우 화면 좌표는 다음과 같습니다.

Sx = x*w + w/2
Sy = y*h + h/2

NDC의 z 구성 요소가 더 이상 필요하지 않다고 말하고 싶습니까?
아니요, 아니요, z 구성 요소도 많은 가치를 가지며 깊이 버퍼 역할을 합니다. 예를 들어 나중에 화면 좌표가 동일한 두 개의 정점이 있고 둘 다 투명하지 않은 경우 깊이 버퍼에 따라 달라지며 카메라에 가장 가까운 정점이 표시됩니다.

강조해야 할 점은 클리핑 공간에서 화면 공간으로의 변환이 맨 아래 레이어에 의해 완료되며 코드 구현이 필요하지 않다는 것입니다.

顶点着色器,只需要把顶点,从模型空间转换到裁减空间即可

조각 셰이더에서는 화면 공간에서 조각의 위치를 ​​얻을 수 있습니다.

4.4 정점좌표변환 요약

섹션 4.2에서 우리는 MVP 행렬에 대해 이야기했고 이제 마침내 이것이 정점을 모델 공간에서 클리핑 공간으로 변환할 수 있는 행렬을 나타낸다는 것을 알게 되었습니다!
이 행렬은 模型矩阵 * 观察矩阵 * 投影矩阵다시 곱해서 얻어지는 것입니다!

그림으로 설명해 보겠습니다.
여기에 이미지 설명을 삽입하세요.

이는 최종적으로 섹션 4.2의 코드와 일치합니다.

o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);

이 매크로는 UNITY_MATRIX_MVP그림의 MVP 매트릭스입니다.

모델 위치/회전/크기를 결정하면 모델 매트릭스가 있습니다. 카메라 위치와 방향이 결정되면 뷰
매트릭스가 있습니다. 카메라의 FOV 화각, 근거리 절단면과 원거리 절단면 사이의 거리 등이 결정되면 Projection Matrix가 생성됩니다.
3개가 모두 결정되고 이를 함께 곱하면 MVP 매트릭스가 결정된다.

따라서 버텍스 셰이더는 매우 간단합니다. 고정된 MVP 매트릭스를 곱하기만 하면 됩니다! 裁减空间그런 다음 아래 좌표를 얻습니다 .
그러면 GPU가 자르기, NDC 좌표 변환, 화면 매핑을 도와 지오메트리 단계가 완벽하게 완료됩니다! !

4.5 비디오 재생을 위한 정점 데이터 검토

비디오 재생의 예인 섹션 3.1을 다시 살펴보면 정점 좌표는 다음과 같습니다.

    private float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f
    };

왜 그렇게 간단합니까? 영상 재생에만 사용되기 때문에 사진은 직사각형입니다. 꼭지점은 4개만 필요하고, 꼭지점 좌표는 2차원이고, 숫자 8개는 꼭지점 4개를 나타내며, z 값의 기본값은 0, w 값의 기본값은 1입니다.

전체 데이터를 직접 작성해야 한다면,

    private float[] vertexData = {
            -1f, -1f, 0f, 1f, 
            1f, -1f,  0f, 1f, 
            -1f, 1f,  0f, 1f, 
            1f, 1f,  0f, 1f, 
    };

따라서 여기의 정점 좌표는 클립 공간의 값과 동일합니다.

즉, 비디오 재생은 2차원이기 때문에 월드 공간이나 관찰 공간에 대해 걱정할 필요가 없으며, 이러한 공간이 없으면 MVP 행렬은 단위 행렬이 됩니다.

또한 w 값이 1이므로 NDC 아래의 좌표는 클리핑 공간과 동일합니다.

4.3.5절에서는 3차원 큐브인 NDC 좌표에 대해 언급했습니다.
z를 고려하지 않으면 NDC 좌표는 2차원 정사각형입니다.

2차원 NDC는 아래 그림과 같습니다.
여기에 이미지 설명을 삽입하세요.
비디오 재생의 정점 좌표를 참조로 사용할 수 있습니다. 즉, 정점은 4개의 값을 사용하며, 일대일 음수임을 보장합니다.

정점에 대해 이야기한 후 OpenGL 텍스처 좌표계에 대해 이야기해 보겠습니다. 텍스처는 2차원일 뿐이며 비디오 재생이든 3D 세계이든 둘 다 2차원입니다.

좌표계는 다음과 같이 정의됩니다.
여기에 이미지 설명을 삽입하세요.

섹션 3.1로 돌아가서 정점 데이터를 로드할 때 해당 텍스처 좌표도 포함합니다.

    //纹理坐标数据
    private float[] fragmentData = {
            0f, 0f,
            1f, 0,
            0f, 1f,
            1f, 1f
    };

데이터를 보면 정점 좌표와 텍스쳐 좌표가 일대일 대응을 하고 있음을 알 수 있는데, 예를 들어 정점 좌표(-1, -1)의 왼쪽 아래 모서리 위치가 왼쪽 아래 모서리 위치에 해당함을 알 수 있습니다. 텍스처 좌표(0, 0).

마지막으로 섹션 3.1에 해당하는 정점 셰이더 코드를 살펴보겠습니다. 이 코드 역시 엄청나게 간단합니다.

attribute vec4 v_Position;
attribute vec4 f_Position;

varying vec2 textureCoordinate;

void main()
{
    gl_Position = v_Position;
    textureCoordinate = f_Position.xy;
}

이는 섹션 4.2와 다릅니다. 이 섹션은 이미 OpenGL용으로 직접 컴파일할 수 있는 셰이더입니다. 섹션 4.2는 Unity에 의해 캡슐화된 코드 형식입니다. 마지막으로 Unity 엔진도 위와 유사한 코드로 변환됩니다. 주요 기능.

v_Position은 외부에서 입력되는 정점 좌표이며 gl_Position클리핑 공간의 최종 좌표를 나타내는 전역 내장 변수입니다.

위의 셰이더가 MVP 행렬을 사용해야 하는 경우에도 가능하지만 이 MVP는 단위 행렬입니다. 단위 행렬을 곱해도 여전히 동일한 값을 갖습니다.
지금 바로

gl_Position = mvpMatrix * v_Position;

5 GPU 처리----래스터화 단계

래스터화의 주요 작업: 각 기본 요소가 어떤 픽셀을 포함하는지 계산한 다음 해당 픽셀에 색상을 지정합니다.

5.1 삼각형 설정 및 순회

삼각형 설정:

이는 래스터화 파이프라인의 첫 번째 단계입니다.
이 단계에서는 삼각형 메시를 래스터화하는 데 필요한 정보를 계산합니다.
구체적으로, 이전 단계의 출력은 화면상의 2차원 삼각형 그리드의 정점이다. 즉, 우리가 얻는 것은 삼각형 메시의 각 모서리에 대한 두 끝점입니다. 그러나 전체 삼각형 격자의 픽셀 범위를 얻으려면 각 가장자리의 픽셀 좌표를 계산해야 합니다. 경계 픽셀의 좌표 정보를 계산하려면 삼각형 경계의 표현을 얻어야 합니다. 이러한 삼각형 메쉬 표현 데이터를 계산하는 과정을 삼각형 설정이라고 합니다. 그 결과는 다음 단계를 준비하는 것입니다.

삼각형 순회:
삼각형 순회 단계에서는 每个像素삼각형 메쉬로 덮여 있는지 확인합니다. 덮이면 프래그먼트가 생성되는데, 삼각형 그리드에 어떤 픽셀이 덮이는지 찾는 과정은 이다 三角形遍历. 삼각형 순회 단계에서는 이전 단계의 계산 결과를 기반으로 어떤 픽셀이 삼각형 메쉬로 덮이는지 결정하고, 삼각형 메쉬의 3개 꼭지점의 꼭지점 정보를 사용하여 덮힌 전체 영역의 픽셀에 대한 연산을 수행합니다 插值. 아래 그림은 삼각형 순회 단계의 단순화된 계산 과정을 보여줍니다.
여기에 이미지 설명을 삽입하세요.
기하학 단계에서 출력된 정점 정보에 따라 삼각형 메쉬에 포함된 픽셀 위치를 얻습니다. 해당 픽셀은 조각을 생성하고 조각의 상태는 삼각형의 정점 정보로부터 계산됩니다.
위 그림처럼 삼각형 격자 안에 총 8개의 조각이 생성됩니다.

궁금하실텐데요,绘制一帧,到底会产生多少片元?

비디오 재생과 같이 단순한 2차원 방식으로만 사용된다면 삼각형은 깊이가 없이 화면의 네 모퉁이에 퍼져 있는 꼭지점 4개만 있다고 할 수 있습니다. 아래 그림과 같이 이해하기 쉽습니다 片元数量就是屏幕的宽*高
.
여기에 이미지 설명을 삽입하세요.
깊이가 같은 삼각형은 총 2개뿐입니다. 즉, 겹치는 삼각형이 없습니다.

하지만 3D 게임이라면 반드시 그런 것은 아닙니다.
여기에 이미지 설명을 삽입하세요.
예를 들어, 게임의 이 장면에는 여러 실제 모델이 있고 모델이 겹칩니다. 예를 들어, 돌 뒤에 집이 있습니다.

프레임을 그리려면 여러 모델을 그려야 하며 각 모델은 Draw Call을 호출해야 할 수도 있습니다. Draw Call은 일부 조각을 생성할 수 있습니다.
모든 객체를 그리면 기본적으로 모든 화면 픽셀 좌표가 그리드에 덮이게 되며 일부 픽셀 좌표에서는 겹치는 조각이 있게 됩니다.
예를 들어 돌의 화살표 영역의 픽셀에 해당하는 삼각형 메쉬와 집의 삼각형 메쉬는 조각을 계산하는데, 둘의 x와 y는 동일하지만 깊이 z는 다릅니다.
그래서,3D游戏场景,片元数量 >= 屏幕宽 * 高

5.2 프래그먼트 셰이더

조각 셰이더는 주로 조각 색상을 지정하는 데 사용되는 사용자 정의 가능한 코드이기도 합니다.
비디오 재생 렌더링을 위한 셰이더 코드부터 시작해 보겠습니다.

varying highp vec2 textureCoordinate;// 片元对应的纹理坐标,又顶点着色器的varying变量会传递到这里

uniform sampler2D sTexture;          // 外部传入的图片纹理 即代表整张图片的数据

void main()
{
     gl_FragColor = texture2D(sTexture, textureCoordinate);  // 从纹理中,找到坐标为textureCoordinate的颜色,赋值给gl_FragColor
}

쉬웠나요? ! 텍스처 좌표를 기반으로 색상을 샘플링하고 조각의 최종 색상으로 gl_FragColor에 할당하면 됩니다!

이는 최종 색상을 완전히 제어할 수 있음을 의미합니다.요즘에는 많은 비디오 특수 효과 처리가 이 프래그먼트 셰이더를 기반으로 합니다.

예를 들어, 비디오를 재생하려고 하는데 흑백 형제의 사진이 보입니다.

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vCoordinate;
uniform samplerExternalOES uTexture;
void main() {
  vec4 color = texture2D(uTexture, vCoordinate);
  float gray = (color.r + color.g + color.b)/3.0;
  gl_FragColor = vec4(gray, gray, gray, 1.0);
}

rgb의 간단한 평균을 만든 다음 최종 색상은 동일한 rgb 구성 요소를 갖습니다(즉, 흰색과 검정색 사이, 회색은 0 검정색, 회색은 255, 흰색, 회색은 둘 사이, 회색). 그러면 다음을 얻을 수 있습니다. 흑백 색상. 그런 다음 이를 조각에 할당하면 간단한 흑백 필터가 완성됩니다.

프래그먼트 셰이더도 병렬로 실행된다는 점을 강조해야 합니다. 존재하는 만큼의 프래그먼트를 실행하면 됩니다! GPU는 매우 강력한 병렬 컴퓨팅 기능을 갖추고 있습니다.

5.3 조각별 작업

PerFragment Operations는 OpenGL이 호출하는 것이며 DirectX에서는 출력 병합 및 혼합 단계가 됩니다.

이 단계의 주요 작업:
(1) 템플릿 테스트, 심층 테스트 등을 수행하여 각 조각의 가시성을 결정합니다!
(2) 조각이 테스트를 통과하고 이 조각의 색상 값이 색상 버퍼에 이미 저장된 색상에 추가되거나 호출됩니다 混合.
여기에 이미지 설명을 삽입하세요.

5.3.1 템플릿 테스트

이는 선택 사항입니다. 템플릿 데이터는 스텐실 버퍼(Stencil Buffer)에 저장됩니다.
템플릿은 무엇을 의미하나요? 아주 간단합니다.예를 들어 장면을 어떻게 그려도 화면 중앙의 원형 영역만 보고 싶고 다른 영역은 보이지 않습니다.

템플릿 예를 들어 보겠습니다.
여기에 이미지 설명을 삽입하세요.
조각의 좌표가 가시 영역 외부에 있으면 바로 삭제됩니다.

5.3.2 깊이 테스트

활성화되면 조각의 깊이 값, 즉 z 값이 깊이 버퍼(있는 경우)에 이미 존재하는 깊이 값과 비교됩니다. 비교 방법을 구성할 수 있습니다. 기본값은 조각이 크거나 같으면 조각을 버리고, 조각이 작거나 같으면 조각을 유지하는 것입니다. 이것도 이해가 갑니다.깊이가 클수록 카메라에서 멀어집니다.카메라에 가까운 것에 의해 차단되므로 표시할 필요가 없습니다.

5.3.3 블렌딩

이전 테스트를 통과했다면 이제 혼합 단계에 들어갈 차례입니다. 이 또한 구성 가능합니다.

병합하는 이유는 무엇입니까? 因为一帧图像生成,需要逐个绘制物体模型,每画一次,就会刷新一次颜色缓冲区。当我们执行某次渲染时,只要不是第一次,那颜色缓冲区就有上一次渲染物体的结果.
그렇다면 동일한 화면 좌표의 위치에 대해서는 마지막 색상을 사용해야 할까요, 이번에는 색상을 사용해야 할까요, 아니면 혼합해야 할까요? 이것이 이 단계에서 고려되는 사항이다.

반투명 객체의 경우 블렌딩 기능을 활성화해야 합니다. 이미 색상 버퍼에 있는 색상을 볼 수 있어야 하고 투명 효과를 얻으려면 블렌딩해야 하기 때문입니다.

불투명한 객체의 경우 개발자는 블렌딩 기능을 끌 수 있으며 결국 깊이 테스트를 통과하는 한 조각은 유지되어야 한다고 일반적으로 명시되어 있으며 조각의 색상을 직접 사용하여 색상을 덮어쓸 수 있습니다. 컬러 버퍼.

5.3.4 이미지 출력-더블 버퍼링

우선, 화면에 표시되는 것은 컬러 버퍼의 데이터입니다.

이미지 프레임을 그리는 과정에서 색상 버퍼는 지속적으로 덮어쓰이고 혼합됩니다. 이 프로세스는 사용자에게 표시되어서는 안 됩니다. 따라서 GPU는 문제를 해결하기 위해 이중 버퍼링 전략을 사용합니다.
FrontBuffer가 그려진 후 화면에 전달되어 표시되고 BackBuffer가 해제되며 BackBuffer는 다음 렌더링에 사용됩니다.
그림을 그리면 명확해질 것입니다.
여기에 이미지 설명을 삽입하세요.
이미지 프레임을 그린 후 eglSwapBuffers를 호출하여 버퍼를 전환합니다.

6 성과토론

6.1 성능 병목 현상은 어디에 있습니까?

먼저 결론부터 이야기하자면 다음과 같습니다.一般在CPU阶段,提交命令的耗时。Draw Call数量越多,越可能造成性能问题。

각 Draw
Call이 호출되기 전에 CPU는 데이터, 상태, 명령 등과 같은 많은 콘텐츠를 GPU로 보내야 합니다. CPU가 준비 작업을 완료하고 명령을 제출하면 GPU가 작업을 시작할 수 있습니다. GPU 렌더링 능력은 매우 강력하여 100개의 삼각형 메시를 렌더링하는 것과 10,000개의 삼각형 메시를 렌더링하는 것에는 큰 차이가 없습니다. 그래서,
GPU渲染速度往往优于CPU提交命令的速度. 많은 경우, GPU는 명령 버퍼 처리를 마치고 유휴 상태로 대기하는 반면, CPU는 여전히 데이터를 준비하기 위해 열심히 일하고 있습니다.

예를 들어 txt 파일이 1,000개 정도 있는데, 1,000개를 다른 폴더에 복사하면 속도가 매우 느려집니다. 압축하지 않더라도 타르볼 패키지로 패키징해서 한번에 다른 폴더에 복사해 놓으면 속도가 더 빨라집니다.
왜? cpy 자체는 매우 빠르기 때문에 각 cpy 전에 메모리 할당, 메타데이터, 파일 컨텍스트 생성 등을 수행하는 데 시간이 많이 걸립니다!

如果地图中有N个物体模型,渲染一帧完整图像,可能就要调用N次Draw Call
, 호출될 때마다 CPU는 많은 준비 작업(렌더링 상태 변경)을 수행해야 하므로 CPU에 부담을 주게 됩니다. 따라서 목표한 성능 최적화가 필요합니다.

6.2 성능을 최적화하는 방법

먼저 Draw Call의 대략적인 CPU 소비 수준을 살펴보겠습니다.
NVIDIA는 GDC에서 초당 25K 배치가 1GHz CPU를 채우고 활용도 100%를 달성할 것이라고 제안한 적이 있습니다.

공식:

DrawCall_Num = 25K * CPU_프레임 * CPU_Percentage / FPS

DrawCall_Num: DrawCall 수(최대 지원)
CPU_Frame: CPU 작동 주파수(GHZ 단위)
CPU_Percentage: CPU가 drawcall에 할당한 시간 비율(백분율)
FPS : 원하는 게임 프레임 속도

예를 들어 Qualcomm 820을 사용하고 작동 주파수가 2GHz이고 CPU 시간의 10%를 드로콜에 할당하며 60개의 프레임이 필요한 경우 프레임은 최대 83개의 드로콜을 가질 수 있습니다(25000 2 10%/60 = 83.33).
) , 할당이 20% CPU 시간인 경우 약 167입니다.

따라서 drawcall의 최적화는 주로 그래픽 인터페이스를 호출할 때 CPU 오버헤드를 최대한 늘리는 것입니다. drawcall에 대한 우리의 주요 아이디어는 다음과 같습니다 每个物体尽量减少渲染次数,多个物体最好一起渲染,或者叫,批处理(batching).

어떤 종류의 객체를 일괄 처리할 수 있나요?
대답은 입니다 使用同一种材质的物体.

동일한 머티리얼의 물리적 특성은 버텍스 데이터만 다를 뿐 사용된 텍스처, 버텍스 셰이더, 프래그먼트 셰이더 코드는 모두 동일하다는 뜻입니다!

따라서 이러한 객체의 정점 데이터를 하나로 병합하고 GPU로 보낸 다음 DrawCall을 다시 호출할 수 있습니다.

Unity 엔진은 두 가지 일괄 처리 방법, 静态批处理즉 및 를 지원합니다 动态批处理. 다른 게임 엔진의 처리 방법은 여기에서 설명하지 않습니다.

6.2.1 Unity 정적 일괄 처리

정적 배치 처리는 모델의 정점 수에 제한을 두지 않으며, 실행 시작 시 한 번만 모델을 병합하고, 모션 시작 시 한 번만 모델을 병합합니다. 이를 위해서는 이러한 물리학이 움직일 수 없어야 합니다!

장점은 한 번 병합하고 만들 수 있으며 후속 렌더링을 위해 병합할 필요가 없으므로 성능이 절약된다는 것입니다.
단점은 더 많은 메모리를 차지할 수 있다는 것입니다. 예를 들어 정적 처리가 없습니다. 一些物体共享了网格
예를 들어 지도에는 모델을 공유하는 100그루의 나무가 있습니다. 이를 정적 일괄 처리로 변환하면 정점 데이터 메모리가 100배 필요합니다.

6.2.2 Unity 동적 일괄 처리

동적 일괄 처리에는 각 렌더링 프레임에 대한 모델 메시 병합이 필요합니다.

장점은 이러한 개체를 이동할 수 있다는 것입니다.
단점은 병합할 때마다 몇 가지 제한 사항이 있다는 것입니다. 예를 들어 정점 수는 300개를 초과할 수 없습니다(Unity 버전에 따라 다름).

7 부록 지식

7.1 동차좌표란 무엇인가

동차 좌표는 N+1 차원을 사용하여 N차원 좌표를 나타냅니다.
예를 들어 데카르트 좌표계의 좌표(x, y, z)는 (x, y, z, w)로 표시됩니다. 좌표는 점일 수도 있고 벡터일 수도 있습니다.

목적:

  1. 区分向量或者点。
  2. 辅助 平移T、旋转R、缩放S这3个最常见的仿射变换

경전 인용문:

동차좌표 표현은 컴퓨터 그래픽의 중요한 방법 중 하나로서 벡터와 점을 명확하게 구분할 수 있을 뿐만 아니라 아핀 기하학적 변환을 보다 쉽게 ​​수행할 수 있게 해줍니다.
—— FS Hill, JR "컴퓨터 그래픽(OpenGL Edition)" 저자

벡터와 점의 구별에 대해서는
(1) 일반좌표를 동차좌표로 변환할 때
(x, y, z)가 점이면 (x, y, z, 1)이 되고,
( x, y,z)가 벡터이면 (x,y,z,0)이 됩니다.

(2) 동차좌표를 일반좌표로 변환할 때
(x,y,z,1)이면 점인 것으로 알고 (x,y,z)가 되고, (x,y,z)
이면 y,z,0 ), 그러면 그것이 벡터라는 것을 알 수 있으며 여전히 (x, y, z)가 됩니다.

이동, 회전, 크기 조정에 대해서는 천천히 이야기해 보겠습니다.

7.2 번역 매트릭스

번역 행렬은 가장 간단한 변환 행렬입니다. 이동 행렬은 다음과 같습니다.
여기에 이미지 설명을 삽입하세요.
여기서 X, Y, Z는 점의 변위 증분입니다. 예를 들어, 벡터 (10, 10, 10, 1) 을 10, 10
을 따라 10 단위 m n의 행렬) n t 의 행렬로만 곱할 수 있습니다 .
여기에 이미지 설명을 삽입하세요.

7.3 스케일링 매트릭스

크기 조정도 매우 간단합니다.
여기에 이미지 설명을 삽입하세요.
예를 들어 벡터(점 또는 방향)를 각 방향에서 2배로 확대합니다.
여기에 이미지 설명을 삽입하세요.

7.4 회전 행렬

이것은 좀 더 복잡하고 X, Y, Z 축을 따른 회전이 다르지만 결국에는 여전히 행렬입니다. 여기서는 별도로 확장하지 않겠습니다. 관심이 있으시면 다음을 참조하세요.

7.5 조합 변환

위에서는 회전, 평행 이동, 크기 조정 작업 방법을 소개했습니다. 이러한 행렬은 곱셈을 통해 결합될 수 있습니다. 예를 들면 다음과 같습니다.

TransformedVector = TranslationMatrix * RotationMatrix * ScaleMatrix *
OriginalVector;

注意,是先执行缩放,接着旋转,最后才是平移。이것이 행렬 곱셈이 작동하는 방식이며, 3D 모델을 지도에 넣는 일반적인 방법이기도 합니다.

세 행렬의 곱셈은 여전히 ​​하나의 행렬입니다. 이는 결합된 변환의 행렬이므로 마지막에 다음과 같이 쓸 수도 있습니다.

TransformedVector = TRSMatrix * OriginalVector;

참고

Android OpenGL ES 1. 기본 개념
컴퓨터 구성 원리 – GPU
[컴퓨터 사물(8) – 그래픽 및 이미지 렌더링 원리](http://chuquan.me/2018/08/26/graphics-rending-principle-gpu/
)
[ opengl-tutorial ](http://www.opengl-tutorial.org/cn/beginners-tutorials/tutorial-3-matrices/
)
[ Unity 문서 ]( https://docs.unity3d.com/cn/2019.4/ 매뉴얼 /SL-
VertexFragmentShaderExamples.html)
래스터화 단계 설정
Drawcall 정보

추천

출처blog.csdn.net/weixin_40301728/article/details/131950662