學(xué)習(xí)OpenGL ES之基本光照

本系列所有文章目錄

獲取示例代碼


本文主要介紹如何使用Shader實現(xiàn)平行光的效果。什么是平行光呢魏滚?我們可以拿激光做比喻,平行光的方向不會隨著離光源的距離而改變谦秧。所以我們在模擬平行光的時候僅僅需要使用一個光照方向即可。
我們有了光照方向撵溃,接下來還需要一個重要數(shù)據(jù)疚鲤,平面的朝向。一個平面如果剛好面朝光線征懈,那自然是最亮的石咬。當然還有些材質(zhì)的平面可以反射光線揩悄,反射光線的強度和你觀察的角度相關(guān)卖哎,不過這些本文都不會介紹。后面會有專門一篇介紹復(fù)雜的光照模型删性。
我們用法線向量來表示平面朝向亏娜,在具體實現(xiàn)中,每個點都會有一個法線向量蹬挺。所謂法線向量就是垂直于平面的一個三維向量维贺,如下圖所示。


圖中展示了兩種法線向量的表示方法巴帮,左邊是每個多邊形的每個點有一個法線向量溯泣,右邊是每個點有一個法線向量虐秋,共享點的法線向量是這個點在所有平面上的法線向量之和。法線向量應(yīng)該總是被規(guī)范化成單位向量垃沦。本文的例子中使用的是左邊的方式客给。

如果你對向量相關(guān)的知識不是很了解,可以參考百度百科

有了法線向量和光照方向之后肢簿,只要將它們相乘即可得到光照強度靶剑。接下來開始分析代碼。

兩個單位向量相乘池充,結(jié)果是cos(向量夾角)桩引,夾角越大,cos(向量夾角)越小收夸,剛好符合前面說的規(guī)律坑匠。

首先我們來看Vertex Shader。

attribute vec4 position;
attribute vec3 normal;

uniform float elapsedTime;
uniform mat4 projectionMatrix;
uniform mat4 cameraMatrix;
uniform mat4 modelMatrix;

varying vec3 fragNormal;

void main(void) {
    mat4 mvp = projectionMatrix * cameraMatrix * modelMatrix;
    fragNormal = normal;
    gl_Position = mvp * position;
}

我將attribute vec4 color;換成了attribute vec3 normal;卧惜,不再傳遞顏色數(shù)據(jù)笛辟,改為法線向量。然后將法線向量傳遞給Fragment ShaderfragNormal = normal;序苏。

接下來是Fragment Shader手幢。

precision highp float;

varying vec3 fragNormal;

uniform float elapsedTime;
uniform vec3 lightDirection;
uniform mat4 normalMatrix;

void main(void) {
    vec3 normalizedLightDirection = normalize(-lightDirection);
    vec3 transformedNormal = normalize((normalMatrix * vec4(fragNormal, 1.0)).xyz);
    
    float diffuseStrength = dot(normalizedLightDirection, transformedNormal);
    diffuseStrength = clamp(diffuseStrength, 0.0, 1.0);
    vec3 diffuse = vec3(diffuseStrength);
    
    vec3 ambient = vec3(0.3);
    
    vec4 finalLightStrength = vec4(ambient + diffuse, 1.0);
    vec4 materialColor = vec4(1.0, 0.0, 0.0, 1.0);
    
    gl_FragColor = finalLightStrength * materialColor;
}

我增加了光線方向uniform vec3 lightDirection;,法線變換矩陣uniform mat4 normalMatrix;忱详。

法線不能直接使用modelMatrix進行變換围来,需要使用modelMatrix的逆轉(zhuǎn)置矩陣,參考維基百科

因為光線是照射到平面的方向匈睁,而法線是從平面往外的方向监透,所以他們相乘之前需要把光照方向反過來,并且要規(guī)范化航唆。

vec3 normalizedLightDirection = normalize(-lightDirection);

接著我們將法線變換后再規(guī)范化胀蛮,我們就得到了關(guān)鍵的兩個向量。下面是示意圖糯钙。


將它們相乘最后得到diffuse粪狼,可以稱它為漫反射強度。漫反射就是投射在粗糙表面上的光向各個方向反射的現(xiàn)象任岸。我們求解diffuse就是模擬的漫反射現(xiàn)象再榄。
代碼最后還有一個vec3 ambient = vec3(0.3);是什么呢?根據(jù)漫反射的公式享潜,總會有強度為0的地方困鸥,為了使場景不那么暗,就增加了一個基本光照強度剑按,也可稱為環(huán)境光強度疾就。
環(huán)境光強度加上漫反射強度就是最后的光照強度finalLightStrength了澜术。光照強度乘以材質(zhì)本身的顏色materialColor得到最終的顏色,這里材質(zhì)本身的顏色我用的是紅色猬腰。

看完Shader瘪板,我們回到OC代碼。首先漆诽,我將綁定Shader屬性color的代碼改為了綁定normal侮攀。

- (void)bindAttribs:(GLfloat *)triangleData {
    // 啟用Shader中的兩個屬性
    // attribute vec4 position;
    // attribute vec4 color;
    GLuint positionAttribLocation = glGetAttribLocation(self.shaderProgram, "position");
    glEnableVertexAttribArray(positionAttribLocation);
    GLuint colorAttribLocation = glGetAttribLocation(self.shaderProgram, "normal");
    glEnableVertexAttribArray(colorAttribLocation);
    
    // 為shader中的position和color賦值
    // glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)
    // indx: 上面Get到的Location
    // size: 有幾個類型為type的數(shù)據(jù),比如位置有x,y,z三個GLfloat元素厢拭,值就為3
    // type: 一般就是數(shù)組里元素數(shù)據(jù)的類型
    // normalized: 暫時用不上
    // stride: 每一個點包含幾個byte兰英,本例中就是6個GLfloat,x,y,z,r,g,b
    // ptr: 數(shù)據(jù)開始的指針供鸠,位置就是從頭開始畦贸,顏色則跳過3個GLFloat的大小
    glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData);
    glVertexAttribPointer(colorAttribLocation, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (char *)triangleData + 3 * sizeof(GLfloat));
}

接著準備法線向量的數(shù)據(jù)。

- (void)drawXPlanes {
    static GLfloat triangleData[] = {
// X軸0.5處的平面
      0.5,  -0.5,    0.5f, 1,  0,  0,
      0.5,  -0.5f,  -0.5f, 1,  0,  0,
      0.5,  0.5f,   -0.5f, 1,  0,  0,
      0.5,  0.5,    -0.5f, 1,  0,  0,
      0.5,  0.5f,    0.5f, 1,  0,  0,
      0.5,  -0.5f,   0.5f, 1,  0,  0,
// X軸-0.5處的平面
      -0.5,  -0.5,    0.5f, -1,  0,  0,
      -0.5,  -0.5f,  -0.5f, -1,  0,  0,
      -0.5,  0.5f,   -0.5f, -1,  0,  0,
      -0.5,  0.5,    -0.5f, -1,  0,  0,
      -0.5,  0.5f,    0.5f, -1,  0,  0,
      -0.5,  -0.5f,   0.5f, -1,  0,  0,
    };
    [self bindAttribs:triangleData];
    glDrawArrays(GL_TRIANGLES, 0, 12);
}

以X軸上的兩個平面為例楞捂,X軸0.5處的平面法線方向是X軸正向薄坏,X軸-0.5處的平面法線方向是X軸反向。這樣我們才能讓朝外的面接收到光線寨闹。法線是三維向量胶坠,所以剛好填滿了之前顏色的數(shù)據(jù)區(qū)間。

下一步準備一個三維向量存放光照的方向繁堡。

@property (assign, nonatomic) GLKVector3 lightDirection; // 平行光光照方向

并給它賦值沈善。讓它向下照射,所以向量為-Y軸(0椭蹄,-1闻牡,0)。

// 設(shè)置平行光方向
self.lightDirection = GLKVector3Make(0, -1, 0);

最后給uniform光照方向和法線變換矩陣賦值绳矩。

bool canInvert;
GLKMatrix4 normalMatrix = GLKMatrix4InvertAndTranspose(self.modelMatrix, &canInvert);
if (canInvert) {
    GLuint modelMatrixUniformLocation = glGetUniformLocation(self.shaderProgram, "normalMatrix");
    glUniformMatrix4fv(modelMatrixUniformLocation, 1, 0, normalMatrix.m);
}


GLuint lightDirectionUniformLocation = glGetUniformLocation(self.shaderProgram, "lightDirection");
glUniform3fv(lightDirectionUniformLocation, 1,self.lightDirection.v);

這里我們使用了GLKit的GLKMatrix4InvertAndTranspose計算modelMatrix的逆轉(zhuǎn)置矩陣罩润,然后傳遞給Shader。傳遞光照方向時使用glUniform3fv來傳遞三維數(shù)組翼馆。

到此割以,基本的平行光光照模型就完成了。下面是效果圖写妥。


下一篇是基礎(chǔ)篇的最后一篇拳球,介紹紋理的加載和使用审姓。后續(xù)會在進階篇中介紹高級光照珍特,3D模型加載,粒子系統(tǒng)等更深入的知識魔吐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扎筒,一起剝皮案震驚了整個濱河市莱找,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗜桌,老刑警劉巖奥溺,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骨宠,居然都是意外死亡浮定,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門层亿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桦卒,“玉大人,你說我怎么就攤上這事匿又》皆郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵碌更,是天一觀的道長裕偿。 經(jīng)常有香客問我,道長痛单,這世上最難降的妖魔是什么嘿棘? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮旭绒,結(jié)果婚禮上蔫巩,老公的妹妹穿的比我還像新娘。我一直安慰自己快压,他們只是感情好圆仔,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蔫劣,像睡著了一般坪郭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脉幢,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天歪沃,我揣著相機與錄音,去河邊找鬼嫌松。 笑死沪曙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的萎羔。 我是一名探鬼主播液走,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缘眶?” 一聲冷哼從身側(cè)響起嘱根,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎巷懈,沒想到半個月后该抒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡顶燕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年凑保,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涌攻。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡愉适,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癣漆,到底是詐尸還是另有隱情维咸,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布惠爽,位于F島的核電站癌蓖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婚肆。R本人自食惡果不足惜租副,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望较性。 院中可真熱鬧用僧,春花似錦、人聲如沸赞咙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽攀操。三九已至院仿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間速和,已是汗流浹背歹垫。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留颠放,地道東北人排惨。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像碰凶,于是被迫代替她去往敵國和親暮芭。 傳聞我的和親對象是個殘疾皇子鹿驼,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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