OpenGLES簡(jiǎn)單入門

OpenGL

一、 基礎(chǔ)概念

1. 簡(jiǎn)介

Android 可通過開放圖形庫(kù) (OpenGL)(特別是 OpenGL ES API)來(lái)支持高性能 2D 和 3D 圖形梅惯。OpenGL 是一種跨平臺(tái)的圖形 API巾表,用于為 3D 圖形處理硬件指定標(biāo)準(zhǔn)的軟件接口笤休。OpenGL ES 是 OpenGL 規(guī)范的一種形式救崔,適用于嵌入式設(shè)備锭部。

2. 坐標(biāo)系

OpenGL ES中圖形的位置是通過坐標(biāo)系確定的囤耳,圖形的繪制方式由點(diǎn)篙顺、線、三角形構(gòu)成充择。在OpenGL中德玫,世界坐標(biāo)系是以屏幕中心為原點(diǎn)(0, 0, 0),且是始終不變的椎麦。當(dāng)我們面對(duì)屏幕宰僧,右邊是x正軸,上面是y正軸观挎,屏幕指向我們?yōu)閦正軸琴儿。長(zhǎng)度單位如下:窗口范圍按此單位是(-1,-1)到(1,1),即屏幕左下角坐標(biāo)為(-1嘁捷,-1)造成,右上角 坐標(biāo)為(1,1)。

3.著色器

著色器是用來(lái)描述頂點(diǎn)或像素特性的程序普气,它使用 GLSL(OpenGL Shading Language)語(yǔ)言開發(fā)谜疤,利用這種語(yǔ)言編寫程序可運(yùn)行在GPU上進(jìn)行圖像的處理或者渲染。著色器分為兩個(gè)部分,即Vertex Shader(頂點(diǎn)著色器)與Fragment Shader(片元著色器)兩部分夷磕,分別完成各自在OpenGL渲染管線中的功能履肃,頂點(diǎn)著色器的主要目的就是確定每個(gè)頂點(diǎn)的最終位置,而片段著色器的主要目的就是告訴GPU每個(gè)片段的最終顏色應(yīng)該是什么坐桩。

4.狀態(tài)機(jī)

狀態(tài)機(jī)是一種行為尺棋,即對(duì)象在其生命周期中響應(yīng)事件所經(jīng)歷的狀態(tài)序列以及對(duì)那些狀態(tài)事件的響應(yīng)。具有以下特點(diǎn):

  • 有記憶功能绵跷,能夠記住當(dāng)前的狀態(tài)
  • 可以接收輸入膘螟,根據(jù)輸入的內(nèi)容和自己的原先狀態(tài),修改自己當(dāng)前狀態(tài)碾局,并且可以有對(duì)應(yīng)輸出
  • 當(dāng)進(jìn)入特殊狀態(tài)(停機(jī)狀態(tài))的時(shí)候荆残,便不再接收輸入,即停止工作

類推到OpenGL中净当,可以這么理解

  • OpenGL可以記錄自己的狀態(tài)(如當(dāng)前所使用的顏色内斯、是否開啟了混合功能等)
  • OpenGL可以接收輸入(調(diào)用OpenGL函數(shù)時(shí),可以理解成OpenGL在接收輸入)像啼,如調(diào)用glColor3f俘闯,即OpenGL接收到這個(gè)輸入后,會(huì)修改自己的“當(dāng)前顏色”這個(gè)狀態(tài)
  • OpenGL可以進(jìn)入停止?fàn)顟B(tài)忽冻,不再接收輸入真朗。在程序退出前,OpenGL總會(huì)先停止工作

5.OpenGL渲染

人眼所看到的圖像是由屏幕上的像素點(diǎn)組成的僧诚,在內(nèi)存中遮婶,這些像素點(diǎn)可以組織成一個(gè)大的一維數(shù)組,每4個(gè)Byte即表示一個(gè)像素點(diǎn)的RGBA數(shù)據(jù)湖笨,而在顯卡中蹭睡,這些像素點(diǎn)可以組織成幀緩沖區(qū)(FrameBuffer)的形式,幀緩沖區(qū)保存了圖形硬件為了控制屏幕上所有像素的顏色和強(qiáng)度所需要的全部信息赶么。
渲染的整體流程:
1. 指定幾何對(duì)象:選擇以何種方式繪制幾何圖形,例如點(diǎn)脊串,面辫呻,三角形等。
2. 頂點(diǎn)處理:通過頂點(diǎn)著色器將本地坐標(biāo)轉(zhuǎn)換為坐標(biāo)系中的坐標(biāo)值琼锋。
3. 組裝圖元:在處理過頂點(diǎn)之后放闺,各個(gè)頂點(diǎn)被按照繪制命令中的規(guī)則組裝成圖元。
4. 光柵化操作:圖元數(shù)據(jù)在此被分成更小的單元并且對(duì)應(yīng)于幀緩沖區(qū)的像素缕坎,一個(gè)單元成為片元怖侦。即從頂點(diǎn)數(shù)據(jù)到顯示到設(shè)備上的像素的過程。
5. 片元處理:通過片元著色器計(jì)算每一一個(gè)片元的顏色值。
6. 幀緩沖操作:執(zhí)行幀緩沖的寫入操作匾寝,負(fù)責(zé)將最終的像素值寫到幀緩沖區(qū)中搬葬。幀緩沖區(qū)簡(jiǎn)單理解就是存儲(chǔ)屏幕上一幀畫面的區(qū)域。

流程如下圖:
流程.jpg

二艳悔、案例-繪制圖片紋理

創(chuàng)建兩個(gè)GLSL代碼段-頂點(diǎn)著色器急凰,片元著色器

OpenGL ES實(shí)現(xiàn)3D繪圖和普通的2D繪圖即view利用canvas來(lái)繪制不一樣,OpenGL需要加載GLSL程式猜年,讓GPU進(jìn)行繪制抡锈。所以需要定義shader代碼,并在初始化的時(shí)候加載乔外。

vertex_shader.glsl

attribute vec4 av_Position;//頂點(diǎn)位置
attribute vec2 af_Position;//紋理位置
varying vec2 v_texPo;//紋理位置與fragment_shader交互
void main() {
    v_texPo = af_Position;
    gl_Position = av_Position;
}

fragment_shader.glsl

precision mediump float;//精度 為float
varying vec2 v_texPo;//紋理位置接收于vertex_shader
uniform sampler2D sTexture;//紋理
void main() {
    gl_FragColor=texture2D(sTexture, v_texPo);
}

申明頂點(diǎn)/紋理坐標(biāo)及其緩沖數(shù)組

    //頂點(diǎn)坐標(biāo)
    var vertexData = floatArrayOf(
        -1f, -1f, 
        1f, -1f,
        -1f, 1f, 
        1f, 1f
    )

    //紋理坐標(biāo)  對(duì)應(yīng)頂點(diǎn)坐標(biāo)與之映射
    var textureData = floatArrayOf(
        0f, 1f,
        1f, 1f,
        0f, 0f,
        1f, 0f
    )

init {
        //這個(gè)buffer是本地代碼中用于存儲(chǔ)頂點(diǎn)矩陣數(shù)據(jù)
        vertexBuffer = ByteBuffer.allocateDirect(vertexData.size * 4)//內(nèi)存大小為vertexData.size*4 頂點(diǎn)都存儲(chǔ)在一個(gè)浮點(diǎn)數(shù)組里床三,每個(gè)浮點(diǎn)有4個(gè)字節(jié)
            .order(ByteOrder.nativeOrder())//字節(jié)緩沖區(qū)按照本地字節(jié)序組織它的內(nèi)容
            .asFloatBuffer()//FloatBuffer類實(shí)例 避免直接操作字節(jié)
            .put(vertexData)//把數(shù)據(jù)從Dalvik的內(nèi)存復(fù)制到本地內(nèi)存
        vertexBuffer.position(0) //設(shè)置緩沖區(qū)起始位置

        //分配紋理坐標(biāo)緩沖
        textureBuffer = ByteBuffer.allocateDirect(textureData.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
            .put(textureData)
        textureBuffer.position(0)
    }

加載GLSL代碼并將其鏈接到程式中

調(diào)用glCreateShader()方法-傳入渲染器類型參數(shù)type,創(chuàng)建對(duì)應(yīng)的著色器對(duì)象杨幼;
調(diào)用glShaderSource()方法-傳入著色器對(duì)象和字符串shaderCode定義的源代碼撇簿,將二者關(guān)聯(lián)起來(lái);
調(diào)用glCompileShader()方法-傳入著色器對(duì)象推汽,對(duì)其進(jìn)行編譯补疑。

 override fun onSurfaceCreated() {
        //創(chuàng)建程式 加載頂點(diǎn)著色器
        program = ShaderUtils.createProgram(
            context.resources,
            "vertex_shader.glsl",
            "fragment_shader.glsl"
        ).also {
            //獲取頂點(diǎn)坐標(biāo)字段
            avPosition = GLES32.glGetAttribLocation(it, "av_Position")
            //獲取紋理坐標(biāo)字段
            afPosition = GLES32.glGetAttribLocation(it, "af_Position")
            //
            bitmapTexture = GLES32.glGetUniformLocation(it, "sTexture")
            //創(chuàng)建要顯示的圖片紋理
            createTextureId(bitmap)
        }
    }

    /**
     * 創(chuàng)建著色器程序?qū)ο?     *
     * @param vertexShaderCode   :頂點(diǎn)著色器代碼
     * @param fragmentShaderCode :片元著色器代碼
     * @return program
     */
    private fun createProgram(
        vertexShaderCode: String?,
        fragmentShaderCode: String?
    ): Int {
        val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        if (vertexShader == 0) {
            return 0
        }
        val fragmentShader =
            loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        if (fragmentShader == 0) {
            return 0
        }
        //創(chuàng)建空的程式
        var program = GLES20.glCreateProgram()
        if (0 != program) {
            //關(guān)聯(lián)兩個(gè)渲染器
            GLES20.glAttachShader(program, vertexShader)
            GLES20.glAttachShader(program, fragmentShader)
            GLES20.glLinkProgram(program)
            val linkStatus = IntArray(1)
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0)
            if (linkStatus[0] != GLES20.GL_TRUE) {
                GLES20.glDeleteProgram(program)
                program = 0
            }
        }
        return program
    }
    
    /**
     * 加載編譯頂點(diǎn)渲染器
     *
     * @param type      :shader類型
     * @param shadeCode :著色器代碼
     * @return shader
     */
    private fun loadShader(type: Int, shadeCode: String?): Int {
        var shader = GLES20.glCreateShader(type)
        if (0 != shader) {
            GLES20.glShaderSource(shader, shadeCode)
            GLES20.glCompileShader(shader)
            val compiled = IntArray(1)
            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0)
            if (compiled[0] == 0) {
                GLES20.glDeleteShader(shader)
                shader = 0
            }
        }
        return shader
    }

創(chuàng)建圖片紋理ID

fun createTextureId(bitmap: Bitmap?): Int {
        var result = 0
        bitmap?.let {
            //生產(chǎn)一個(gè)紋理
            val textureIds = IntArray(1)
            GLES32.glGenTextures(1, textureIds, 0)
            //綁定為 2D紋理
            GLES32.glBindTexture(GLES32.GL_TEXTURE_2D, textureIds[0])
            //設(shè)置環(huán)繞模式
            GLES32.glTexParameteri(GLES32.GL_TEXTURE_2D, GLES32.GL_TEXTURE_WRAP_S, GLES32.GL_REPEAT)
            GLES32.glTexParameteri(GLES32.GL_TEXTURE_2D, GLES32.GL_TEXTURE_WRAP_T, GLES32.GL_REPEAT)
            //設(shè)置過濾模式
            GLES32.glTexParameteri(
                GLES32.GL_TEXTURE_2D,
                GLES32.GL_TEXTURE_MIN_FILTER,
                GLES32.GL_LINEAR
            )
            GLES32.glTexParameteri(
                GLES32.GL_TEXTURE_2D,
                GLES32.GL_TEXTURE_MAG_FILTER,
                GLES32.GL_LINEAR
            )
            //綁定 bitmap到 textureIds[0] 這個(gè)2D紋理上
            GLUtils.texImage2D(GLES32.GL_TEXTURE_2D, 0, bitmap, 0)

            val bitmapBuffer = ByteBuffer.allocate(bitmap.height * bitmap.width * 4)
            bitmap.copyPixelsToBuffer(bitmapBuffer)
            //將bitmapBuffer位置移動(dòng)到初始位置
            bitmapBuffer.flip()
            //設(shè)置內(nèi)存大小綁定內(nèi)存地址
            GLES32.glTexImage2D(
                GLES32.GL_TEXTURE_2D, 0, GLES32.GL_RGBA, bitmap.width, bitmap.height,
                0, GLES32.GL_RGBA, GLES32.GL_UNSIGNED_BYTE, bitmapBuffer
            )
            //退出 紋理的設(shè)置,進(jìn)入下一環(huán)節(jié)
            GLES32.glBindTexture(GLES32.GL_TEXTURE_2D, 0)
            result = textureIds[0]
        }
        return result
    }

繪制紋理到屏幕上

override fun onDrawFrame() {
        //清空顏色
        GLES32.glClear(GLES32.GL_COLOR_BUFFER_BIT)
        //設(shè)置背景顏色
        GLES32.glClearColor(1f, 1f, 1f, 1f)
        //使用GLSL程式
        GLES32.glUseProgram(program)
        //繪制視頻源  流程為->配置歹撒、綁定id莲组、釋放
        GLES32.glEnableVertexAttribArray(avPosition)
        GLES32.glEnableVertexAttribArray(afPosition)
        //頂點(diǎn)著色器的紋理/頂點(diǎn)坐標(biāo)
        GLES32.glVertexAttribPointer(avPosition, 2, GLES32.GL_FLOAT, false, 0, vertexBuffer)
        GLES32.glVertexAttribPointer(afPosition, 2, GLES32.GL_FLOAT, false, 0, textureBuffer)
        //傳入圖片紋理
        GLES32.glBindTexture(GLES32.GL_TEXTURE_2D, bitmapTexture)
        GLES32.glDrawArrays(GLES32.GL_TRIANGLE_STRIP, 0, 4)
        //釋放
        GLES20.glDisableVertexAttribArray(avPosition)
        GLES20.glDisableVertexAttribArray(afPosition)
        GLES32.glBindTexture(GLES32.GL_TEXTURE_2D, 0)
        GLES32.glBindBuffer(GLES32.GL_ARRAY_BUFFER, 0)
    }

三、更多

GLSL基礎(chǔ)語(yǔ)法

  • GLSL是一種面向過程的語(yǔ)言暖夭,和Java的面向?qū)ο笫遣煌摹?/p>

  • GLSL的基本語(yǔ)法與C/C++基本相同锹杈。

  • 它完美的支持向量和矩陣操作。

  • 它是通過限定符操作來(lái)管理輸入輸出類型的迈着。(in竭望、out)

  • GLSL提供了大量的內(nèi)置函數(shù)來(lái)提供豐富的擴(kuò)展功能。

  • GLSL中的數(shù)據(jù)類型主要分為標(biāo)量裕菠、向量咬清、矩陣、采樣器奴潘、結(jié)構(gòu)體兰粉、數(shù)組喉誊、空類型七種類型:

//標(biāo)量(只有大小沒有方向)
float a=1.0;
int b=1;
bool c=true;

vec2 d=vec2(1.0,2.0);
vec3 e=vec3(1.0,2.0,3.0)
vec4 f=vec4(vec3,1.2);
vec4 g=vec4(0.2);  //相當(dāng)于vec(0.2,0.2,0.2,0.2)
vec4 h=vec4(a,a,1.3,a);
//矩陣
mat2 i=mat2(0.1,0.5,1.2,2.4);
mat2 j=mat2(0.8);   //相當(dāng)于mat2(0.8,0.8,0.8,0.8)
mat3 k=mat3(e,e,1.2,1.6,1.8);
  • 類型轉(zhuǎn)換
    GLSL的類型轉(zhuǎn)換與C不同御蒲。在GLSL中類型不可以自動(dòng)提升腻格,比如float a=1;就是一種錯(cuò)誤的寫法,必須嚴(yán)格的寫成float a=1.0奈虾,也不可以強(qiáng)制轉(zhuǎn)換夺谁,即float a=(float)1;也是錯(cuò)誤的寫法廉赔,但是可以用內(nèi)置函數(shù)來(lái)進(jìn)行轉(zhuǎn)換,如float a=float(1);還有float a=float(true);(true為1.0匾鸥,false為0.0)等蜡塌,值得注意的是,低精度的int不能轉(zhuǎn)換為低精度的float扫腺。

  • 限定符

  • attritude:一般用于各個(gè)頂點(diǎn)各不相同的量岗照。如頂點(diǎn)顏色、坐標(biāo)等笆环。

  • uniform:一般用于對(duì)于3D物體中所有頂點(diǎn)都相同的量攒至。比如光源位置,統(tǒng)一變換矩陣等躁劣。

  • varying:表示易變量迫吐,一般用于頂點(diǎn)著色器傳遞到片元著色器的量。

  • const:常量账忘。

浮點(diǎn)精度

image-20200408225702185
  • lowp:低精度志膀。8位。
  • mediump:中精度鳖擒。10位溉浙。
  • highp:高精度。16位蒋荚。

內(nèi)建變量

gl_Position: 用于vertex shader, 寫頂點(diǎn)位置戳稽;被圖元收集、裁剪等固定操作功能所使用期升;
           其內(nèi)部聲明是:highp vec4 gl_Position;
gl_PointSize: 用于vertex shader, 寫光柵化后的點(diǎn)大小惊奇,像素個(gè)數(shù);
           其內(nèi)部聲明是:mediump float gl_Position;
gl_FragColor: 用于Fragment shader播赁,寫fragment color颂郎;被后續(xù)的固定管線使用;
            mediump vec4 gl_FragColor;
gl_FragData: 用于Fragment shader容为,是個(gè)數(shù)組乓序,寫gl_FragData[n] 為data n;被后續(xù)的固定管線使用坎背;
            mediump vec4 gl_FragData[gl_MaxDrawBuffers];
gl_FragColor和gl_FragData是互斥的竭缝,不會(huì)同時(shí)寫入;
gl_FragCoord: 用于Fragment shader,只讀沼瘫, Fragment相對(duì)于窗口的坐標(biāo)位置 x,y,z,w; 這個(gè)是固定管線圖元差值后產(chǎn)生的;z 是深度值; mediump vec4 gl_FragCoord;
gl_FrontFacing: 用于判斷 fragment是否屬于 front-facing primitive咙俩;只讀耿戚;
              bool gl_FrontFacing;   
gl_PointCoord: 僅用于 point primitive; mediump vec2 gl_PointCoord;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末湿故,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子膜蛔,更是在濱河造成了極大的恐慌坛猪,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂股,死亡現(xiàn)場(chǎng)離奇詭異墅茉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呜呐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門就斤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蘑辑,你說我怎么就攤上這事洋机。” “怎么了洋魂?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵绷旗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我副砍,道長(zhǎng)衔肢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任豁翎,我火速辦了婚禮角骤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谨垃。我一直安慰自己启搂,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布刘陶。 她就那樣靜靜地躺著胳赌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匙隔。 梳的紋絲不亂的頭發(fā)上疑苫,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音纷责,去河邊找鬼捍掺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛再膳,可吹牛的內(nèi)容都是我干的挺勿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼喂柒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼不瓶!你這毒婦竟也來(lái)了禾嫉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚊丐,失蹤者是張志新(化名)和其女友劉穎熙参,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體麦备,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孽椰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凛篙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黍匾。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鞋诗,靈堂內(nèi)的尸體忽然破棺而出膀捷,到底是詐尸還是另有隱情,我是刑警寧澤削彬,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布全庸,位于F島的核電站,受9級(jí)特大地震影響融痛,放射性物質(zhì)發(fā)生泄漏壶笼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一雁刷、第九天 我趴在偏房一處隱蔽的房頂上張望覆劈。 院中可真熱鬧,春花似錦沛励、人聲如沸责语。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坤候。三九已至,卻和暖如春企蹭,著一層夾襖步出監(jiān)牢的瞬間白筹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工谅摄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒河,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓送漠,卻偏偏與公主長(zhǎng)得像顽照,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闽寡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 使用OpenGL ES最關(guān)心的問題 1代兵、如何在iOS上搭建OpenGL ES環(huán)境2纵穿、如何鏈接GLSL3、如何通過G...
    啵啵_long_港閱讀 2,264評(píng)論 0 1
  • 早就聽過大名鼎鼎的 OpenGL奢人,卻遲遲沒有實(shí)踐學(xué)習(xí),有些慚愧淆院。今天開始通過實(shí)踐+博文方式學(xué)習(xí)掌握 OpenGL何乎。...
    王英豪閱讀 22,626評(píng)論 1 33
  • 一、認(rèn)識(shí)OpenGL相關(guān)概念 OpenGL:用于創(chuàng)建3d圖像的編程接口土辩,用于PC端支救,是一個(gè)跨編程語(yǔ)言、跨平臺(tái)的圖形...
    紫水依閱讀 1,143評(píng)論 0 2
  • 轉(zhuǎn)自http://www.cocoachina.com/game/20150811/12969.html 譯序 早...
    LonelyOldMan閱讀 2,442評(píng)論 0 4
  • 在上篇文章GLSL初始著色器語(yǔ)言中已經(jīng)介紹過如何編寫一個(gè)著色器文件拷淘,以及如何連接程序?qū)ο蠛椭鞯膶?duì)象的方法函數(shù)各墨,...
    Henry_Jeannie閱讀 944評(píng)論 0 1