OpenGL ES on iOS --- 光照進階

簡述

本文記錄我記錄我學習 座標體系和矩陣轉換的過程,加深學習便於後續查詢,可能有些描述不夠準確,或者內容不夠充實,還請多多指正,共同窗習.bash

光源分類

在基礎光照時,學習了光照對物體的做用,也就至關於物體的材質,此次主要說 現實生活中的光源函數

平行光

當光源無限遠時,從其發射過來的的光能夠近似的看作平行光(例如太陽);這時 光線的方向都是一致的.學習

平行光

float ambientStrength = 0.3;    //環境因子
    float specularStrength = 2.0;
    float reflectance = 256.0;

    //平行光方向
    vec3 paraLightDir = normalize(vec3(-0.2,-1.0,-0.3));

    //環境光
    vec3 ambient = ambientStrength * texture(Texture,outTexCoord).rgb;

    //漫反射
    vec3 norm = normalize(outNormal);
    vec3 lightDir = normalize(lightPo - FragPo);    //當前頂點 至 光源的的單位向量
    float diff = max(dot(norm,paraLightDir),0.0);
    vec3 diffuse = diff * lightColor*texture(Texture,outTexCoord).rgb;

    //鏡面反射
    vec3 viewDir = normalize(viewPo - FragPo);
    vec3 reflectDir = reflect(-paraLightDir,outNormal);
    float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
    vec3 specular = specularStrength * spec * texture(specularTexture,outTexCoord).rgb;

    //光線衰弱
    float constantPara = 1.0f;
    float linearPara = 0.09f;
    float quadraticPara = 0.032f;
    float LFDistance = length(lightPo - FragPo);
    float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

    vec3 res = ambient + diffuse + specular;

    FragColor = vec4(res,1.0);
複製代碼

點光源

點光源就是比較正常的光源,光從光源四散發出,光線的向量就等於光源到物體的向量. ui

點光源

float ambientStrength = 0.3;    //環境因子
    float specularStrength = 2.0;
    float reflectance = 256.0;

    float constantPara = 1.0f;    //常亮
    float linearPara = 0.09f;     //線性部分因數
    float quadraticPara = 0.032f; //二次項部分因數

    //環境光
    vec3 ambient = ambientStrength * texture(Texture,outTexCoord).rgb;

    //漫反射
    vec3 norm = normalize(outNormal);
    vec3 lightDir = normalize(lightPo - FragPo);    //當前頂點 至 光源的的單位向量

    //點光源
    float diff = max(dot(norm,lightDir),0.0);   //光源與法線夾角
    vec3 diffuse = diff * lightColor*texture(Texture,outTexCoord).rgb;

    //鏡面反射
    vec3 viewDir = normalize(viewPo - FragPo);
    vec3 reflectDir = reflect(-lightDir,outNormal);

    float spec = pow(max(dot(viewDir, reflectDir),0.0),reflectance);
    vec3 specular = specularStrength * spec * texture(specularTexture,outTexCoord).rgb;

    float LFDistance = length(lightPo - FragPo);
    float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

    vec3 res = (ambient + diffuse + specular)*lightWeakPara;

    FragColor = vec4(res,1.0);
複製代碼

聚光源

聚光源的效果就至關於 手電筒,比如朝向指定範圍的點光源~ spa

聚光源

在使用聚光源時,就須要指定 聚光朝向SpotDir,和切光角ϕ. 當光源指向點的向量和SpotDir的夾角大於ϕ時,則沒法被光源照射到.3d

可是這樣的明暗邊界十分明顯,效果不夠真實code

初始版本

這時,咱們就須要將過渡邊緣平滑,這時 咱們就須要引入兩個參數, 內錐角和外錐角. 外錐角就是切光角,而內錐角之內不須要平滑效果, 內錐角和外錐角之間須要平滑過分. 代碼以下orm

/(一些複雜的計算操做 應該讓CPU作,提升效率,不變的量也建議外部傳輸,避免重複計算)
    float inCutOff = cos(radians(10.0f));   //內錐角cos值
    float outCutOff = cos(radians(15.0f));  //外錐角cos值
    vec3 spotDir = vec3(-1.2f,-1.0f,-2.0f); //聚光朝向
    
    float theta = dot(lightDir,normalize(-spotDir));    //光源指向物體的向量 和 聚光朝向的 cos值
    float epsilon  = inCutOff - outCutOff;  //內外錐角cos差值
    
    //clamp(a,b,c);若b<a<c 則函數返回值爲a 若不是,則返回值最小爲b 最大爲c
    // (theta - outCutOff)/epsilon 若theta的角度小於內錐角 則其值>=1 若theta的角度大於外錐角 則其值<=0 這樣光線就在內外錐角之間平滑變化.
    float intensity = clamp((theta - outCutOff)/epsilon,0.0,1.0);
複製代碼

結果

光線衰弱

在現實狀況中,光源發出的光線是會隨着距離的增加而衰減的, 並且也不是線性衰減的,表現爲在距離光源近的這段距離衰減的較快, 在距離光源較遠的狀況下衰減較慢. 一般使用這個公式來模擬光線衰減.cdn

衰減公式

常數項一般保持爲1.0,它的主要做用是保證分母永遠不會比1小,不然的話在某些距離上它反而會增長強度,這確定不是咱們想要的效果 一次項會與距離值相乘,以線性的方式減小強度 二次項會與距離的平方相乘,讓光源以二次遞減的方式減小強度。二次項在距離比較小的時候影響會比一次項小不少,但當距離值比較大的時候它就會比一次項更大了blog

效果展現

參數距離表

float LFDistance = length(lightPo - FragPo);
    float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));

    vec3 res = (ambient + diffuse + specular)*lightWeakPara;
複製代碼

光照貼圖

在一張紋理圖中,因爲材質不一樣,所呈現的效果也會有所不一樣,如同下面這個箱子,金屬邊框和木頭在相同光源下所呈現的1效果確定有所不一樣.

木箱紋理

這時爲了在顯示光照效果時將其區分開來,則須要引入光照貼圖的概念~ 以下圖

鏡面光照貼圖

在該貼圖中,對應木頭部分爲黑色vec3(0.0); 而在金屬邊框部分 則對應的爲灰色, 這樣在計算 漫反射或者鏡面時,將其做爲參考系數,則可讓其呈現不一樣的效果.

vec3 spe = texture(specularTexture,outTexCoord).rgb;   //獲取鏡面光照貼圖
 
vec3 viewDir = normalize(viewPo - FragPo);
vec3 reflectDir = reflect(-lightDir,outNormal);

float spec = pow(max(dot(viewDir, reflectDir),0.0),spL.reflectance);
vec3 specular = point_specularStrength * spec * spe;    //使用光照貼圖紋理
複製代碼

鏡面光照紋理效果
是能夠看出箱子鐵框的鏡面效果 比 木頭的效果要強
相關文章
相關標籤/搜索