Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android OpenGL ES零基礎系列(三):OpenGL ES的渲染管道及VertexShader與FragmentShader

Android OpenGL ES零基礎系列(三):OpenGL ES的渲染管道及VertexShader與FragmentShader

編輯:關於Android編程

前言

在前2篇文章中,我們都說到著色器,且在第二篇中正式說到,這著色器只能用在OpenGL ES2.x等可編程管道裡面,而在OpenGL ES1.x是不能用的。但我們一直沒有說這是為什麼,兩者有什麼區別。那這篇我們就一起來學習下OpenGL ES中的渲染管道。

正文

管道,英文名叫Pipeline,相信用過FaceBook圖片加載庫的同學對這個管道並不陌生,因為SimpleImageDrawee裡面也是用的管道來對圖片進行的一個處理。由於其底層也是C,因此我可以大膽的猜想,FaceBook圖片加載庫的設計思路可能有參考OpenGL(這當然純屬臆想^_^)。
管道用正確的計算機語言來描述就是:
顯卡執行的、從幾何體到最終渲染圖像的、數據傳輸處理計算的過程。

即是管道,那就得有先後順序。整體是從上游流到下游。
在OpenGL ES1.x中,它是固定管道,整體是封閉的,中間的各道工藝按固定的流程順序走。看下圖:
圖一

從上圖可以看出,這些工藝順序是固定的。整個過程又可以分成三部分:處理頂點、處理片元、驗證片元信息並存入內存。
Rasterizer:光柵化處理,當頂點處理完後,會交給rasterizer來進行光柵化處理,結果會把頂點的坐標信息轉換成能在屏幕顯示的像素信息即片元(fragments)。生成片元後,接下來就是對fragments片元的各種驗證,即過濾掉無用的片元,裁剪掉不在視野內的片元,最終把有效片元存儲入內存中。

這裡對於Rasterizer光柵化,讓我們一起來了解學習下:

Rasterizer/Rasterization:光柵化處理

這個詞兒Adobe官方翻譯成柵格化或者像素化。沒錯,就是把矢量圖形轉化成像素點兒的過程。我們屏幕上顯示的畫面都是由像素組成,而三維物體都是點線面構成的。要讓點線面,變成能在屏幕上顯示的像素,就需要Rasterize這個過程。就是從矢量的點線面的描述,變成像素的描述。(或:所頂點從世界坐標系轉換為屏幕坐標系的片元)
如下圖,這是一個放大了1200%的屏幕,前面是告訴計算機我有一個圓形,後面就是計算機把圓形轉換成可以顯示的像素點。這個過程就是Rasterize。

圖二

現在是一個多元化的社會,是一個講個性化的社會,什麼都想著個性化,OpenGL ES也不例外,它為個性化的需求提供了接口。如圖一中的藍色方塊部分,就是可以高度定制化的地方,因此也就形成了OpenGL ES2.x等的可編程管道,在OpenGL ES裡面有兩個專用的詞VertexShader(頂點著色器)、FragmentShader(片元著色器),分別對應圖一中的Coordinate藍色塊和Texture等藍色塊。

下面就看下OpenGL ES2.0 可渲染管道圖:

圖三

VertexShader:頂點著色器

頂點著色器,記得在前2篇中,我們有貼出2個著色器的腳本語句,再次貼出如下:

 /**
     * 頂點著色器的語句
     */
    private final String mVertexShader =
            "uniform mat4 uMVPMatrix;\n" +
                    "attribute vec4 aPosition;\n" +
                    "attribute vec2 aTextureCoord;\n" +
                    "varying vec2 vTextureCoord;\n" +
                    "void main() {\n" +
                    "  gl_Position = uMVPMatrix * aPosition;\n" +
                    "  vTextureCoord = aTextureCoord;\n" +
                    "}\n";

    /**
     * 片元著色器的語句
     */
    private final String mFragmentShader =
            "precision mediump float;\n" +
                    "varying vec2 vTextureCoord;\n" +
                    "uniform sampler2D sTexture;\n" +
                    "void main() {\n" +
                    "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
                    "}\n";

下面看下vertexShader語句中的關鍵字:
attribute :使用頂點數組封裝每個頂點的數據,一般用於每個頂點都各不相同的變量,如頂點位置、顏色等
uniform : 頂點著色器使用的常量數據,不能被著色器修改,一般用於對同一組頂點組成的單個3D物體中所有頂點都相同的變量,如當前光源的位置。
sampler:這個是可選的,一種特殊的uniform,表示頂點著色器使用的紋理。
mat4: 表示4×4浮點數矩陣,該變量存儲了組合模型視圖和投影矩陣
vec4:表示包含了4個浮點數的向量
varying是用於從頂點著色器傳遞到片元著色器或FragmentShader傳遞到下一步的輸出變量
uMVPMatrix * aPosition:通過4*4的變換矩陣變換位置後,輸出給gl_Position。gl_Position是頂點著色器內置的輸出變量。gl_FragColor:是片元著色器內置的輸出變量。

PrimitiveAssembly:圖元裝配

圖元即圖形,在OpenGL中有幾個基本圖元:點,線,三角形,其它的復雜圖元都是基於這些基本圖元來繪成的。
在圖元裝配不為階段,那些經過頂點著色器(VertexShader)處理過的頂點數組或緩沖區的數據(VertexArrays/Buff Objects),被組裝到一個個獨立的幾何圖形中(eg:點,線,三角形等)。
對裝配好的每一個圖元,都必須確保它在世界坐標系(即能顯示在屏幕的可見區域)中,而對於不在世界坐標系中的圖元,就必須進行裁剪,使其處在世界坐標系中才能流到下一道工序(光柵化處理)。
在這裡注意下還有一個剔除操作(Cull),前提是這個功能的開關是打開的:GLES20.glEnable(GLES20.GL_CULL_FACE);,剔除的是圖元的背影,陰影,背面等。

FragmentShader:片元著色器

片元著色器主要是對光柵化處理後生成的片元逐個進行處理。接收頂點著色器輸出的值,需要傳入的數據,以及它經過變換矩陣後輸出值存儲在哪裡可以通過 下圖一目了然:

這裡寫圖片描述

gl_FragColor:是片元著色器內置的輸出變量。

因為Rasterization光柵化處理後,圖元只是在屏幕有了象素,卻還沒有進行顏色處理,還是看不到東西。
因此FragmentShader可以理解為:告訴電腦如何上色的——如何處理光、陰影、遮擋、環境等等。

Per-Fragment Operations:逐個片元操作階段

在片元著色器對片元進行綜合的處理,並最終為片元生成一個顏色值並存儲在gl_FragColor變量後,接下來就是逐個對片元進行一系列的測試。在上面我們說到,在光柵化處理時,它由於是把頂點從世界坐標系轉換到屏幕坐標系,因此在光柵處理後,每個片元在屏幕上都有個坐標(Xw,Yw).且存儲在了幀緩沖區(FrameBuffer),包括片元著色器也是對(Xw,Yw)這個坐標的片元進行處理。
下圖展示了Per-Fragment Operations的過程:

圖五

Pixel ownership test:像素所有權測試,它決定FrameBuffer中某一個(Xw, Yw)位置的像素是否屬於當前 Opengl ES的context,比如:如果一個Opengl ES幀緩沖窗口被其他窗口遮住了,窗口系統將決定被遮住的像素不屬於當前Opengl ES的context,因此也就不會被顯示。

Scissor test:裁剪測試決定,判斷某一個位置為(Xw, Yw)的片元是否位於裁剪矩形內,如果不在,則被丟棄。
Stencil Test / Depth tests:模板和深度測試,傳入片元的模板和深度值,決定是否丟棄片元。
Blending:將FragmentShader新產生的片元顏色值和FrameBuffer中某一個位置為(Xw, Yw)的片元存儲的顏色值進行混合。
Dithering:對於可用顏色較少的系統,可以以犧牲分辨率為代價,通過顏色值的抖動來增加可用顏色數量。抖動操作是和硬件相關的,OpenGL允許程序員所做的操作就只有打開或關閉抖動操作。實際上,若機器的分辨率已經相當高,激活抖動操作根本就沒有任何意義。要激活或取消抖動,可以用glEnable(GL_DITHER)glDisable(GL_DITHER)函數。默認情況下,抖動是激活的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved