my rendering engine
https://github.com/Bly7/OBJ-Loader/blob/master/Source/OBJ_Loader.h
只有一個.h檔案,沒有依賴其他lib,我用他來讀取obj檔並寫入自製的三角形結構。
- 複製以下指令即可編譯
mkdir build
cd build
cmake ..
make
接下來給他Data資料夾中格式的資料
./Lab4 lab4D.in
如果是第一次運行的話,可能需要先下載openGL和glut
sudo apt-get install libgl1-mesa-dev
sudo apt-get install freeglut3-dev
- 光照模型
Vector3d shader(double _kd, double _ks, double n,
Vector3d view_pos, Vector3d eye_pos, Vector3d color,
Vector3d normal, Vector3d amb_light_intensity,
const std::vector<Light> &lights) {
normal = unit_vector(normal);
Vector3d result_color = {0, 0, 0};
double diffuse = 0, specular = 0;
for (auto &light: lights) {
if (!light.is_used) continue;
Vector3d light_dir = unit_vector(light.point - view_pos);
double normal_dot_light = abs(dot(normal, light_dir));
Vector3d reflect = unit_vector(2 * normal_dot_light * (normal - light_dir));
Vector3d view_dir = unit_vector(eye_pos - view_pos);
diffuse += _kd * normal_dot_light;
specular += _ks * pow(abs(dot(reflect, view_dir)), n);
}
for (int i = 0; i < 3; i++) {
result_color[i] += (amb_light_intensity[i] + diffuse) * color[i] + specular;
}
return result_color;
}
- 根據z-buffer進行計算
for (int j = x_min; j <= x_max; j++) {
for (int k = y_min; k <= y_max; k++) {
if (isInTriangle(points[0],
points[1],
points[2],
Vector4d(j + 0.5, k + 0.5, 0.0, 0.0))) {
auto [alpha, beta, gamma] = computeBarycentric2D(j + 0.5, k + 0.5, points);
// cout << alpha << " " << beta << " " << gamma << endl;
auto Z = 1.0 / (alpha / points[0].w() + beta / points[1].w() + gamma / points[2].w());
auto zp = alpha * points[0].z() / points[0].w() + beta * points[1].z() / points[1].w() +
gamma * points[2].z() / points[2].w();
zp *= Z;
// cout << zp << endl;
if (zp < z_buffer[j][k]) {
auto interpolated_color = interpolate(alpha, beta, gamma,
obj.triangles[i]->color[0],
obj.triangles[i]->color[1],
obj.triangles[i]->color[2],
alpha + beta + gamma);
drawDot(j, k, interpolated_color);
z_buffer[j][k] = zp;
}
}
}
}
- shader (雖然說是shader,但感覺給的參數怪怪的,所以有部分乾脆自己土炮,但應該是對的)
Vector3d blinn_phong_shader(double _kd, double _ks, double n,
Vector3d view_pos, Vector3d eye_pos, const Vector3d color,
Vector3d normal, Vector3d amb_light_intensity,
const std::vector<Light> &lights) {
Vector3d result_color = {0, 0, 0};
normal = unit_vector(normal);
for (auto &light: lights) {
if (!light.is_used) continue;
Vector3d light_dir = view_pos - light.point;
Vector3d view_dir = view_pos - eye_pos;
/// 光到物體的距離
double r = light_dir.length();
/// 半程向量
Vector3d h = unit_vector(light_dir + view_dir);
/// 漫反射
Vector3d ld = (_kd * light.intensity * 20.0 / (r * r)) * std::max(0.0, dot(normal, light_dir));
ld = ld * color;
/// 高光的衰減程度
double p = 150.0;
/// 高光的能量
Vector3d ls = (_ks * light.intensity * 20.0 / (r * r)) * pow(std::max(0.0, dot(normal, h)), p);
ls = ls;
result_color += (ld + ls);
}
/// 環境光
Vector3d la = amb_light_intensity; // la = KaIa
result_color += la * color;
return result_color;
}
光源太遠了,光會/(r^2)的速度衰弱,光強度係數(在shader.h中)改成90出來的結果。
以上述方式生成的 build 中開啟終端機,找到執行檔。(不要移動它的相對位置)
我自己是採用跟助教一樣的方式,直接透過寫好相對路徑來執行,以下指令就可以跑了。
./Lab4 lab4A.in
由於使用fgetc(stdin);來進行暫停,所以輸入任意鍵就可以繼續往下跑了,所有運算過程都有對過和列出來。
- Matrix4d : 4 * 4 矩陣
- Vector4d : 4 個 double的陣列
- Vector3d : 3 個 double的陣列
- Triangle : 利用Vector3d和Vector4d來存放三角形和其屬性
- Math : 做一點數學運算
- Main : 進行主要運算和繪製圖形
- Shader : 放 shader 函數
出現#
時,不須執行該行的內容
要注意開頭會給兩個數字,分別是window
的width
和height
清除transform_matrix
輸入的指令為 translate x y z
x
代表沿著x
軸移動x
單位
y
代表沿著y
軸移動y
單位
z
代表沿著z
軸移動z
單位
輸入的指令為 scale x y z
x
代表沿著x
軸縮放x
單位
y
代表沿著y
軸縮放y
單位
z
代表沿著z
軸縮放z
單位
輸入的指令為 rotate x y z
x
代表沿著x
軸旋轉x
度
y
代表沿著y
軸旋轉y
度
z
代表沿著z
軸旋轉z
度
:::warning
規定先對y
軸旋轉,再來是 z
軸,最後 x
軸。
:::
清除所創建的物件(obj
)
清除畫面
輸入的指令為 view vxl vxr vyb vyt
因為經過 perspective divide
因此所有的圖形都會落在
vxl vxr vyb vyt
為映射後的位置
簡單來說要符合 $$ f(wxl,wyb) = (vxl,vyb)\ f(wxr,wyt) = (vxr,vyt)\ $$
在view
外面的圖形要做剪裁
在 display
才需要畫圖
新增 指令為 object obj Or Og Ob Kd Ks N
object 後面接的是檔案名稱,請把obj檔案內的所有的面都讀入你的stack
中,並將圖形切成三角形(此次檔案只有四邊形及三角形)。
Or Og Ob
為物體的顏色,色彩空間為RGB
Kd
為漫反射係數
Ks
為高光係數
N
為光澤度 (gloss
)
指令為 observer epx epy epz COIx COIy COIz Tilt Hither Yon Hav
設定攝影機的位置
epx epy epz
為攝影機的 x y z
位置
COIx COIy COIz
為攝影機看的點, 即 Tilt
為攝影機傾斜的角度。
:::warning
攝影機的 forward
、up
、right
應該是正交的。
Hither
為 near
的平面
Yon
為 far
的平面
Hav
為 FOV
即 field of view
在做display的時候就做一次clearScreen
:::info
著色頻率使用Flat Shading
:::
請在每一次display
東西出來時,加上system("pause");
,或者其他更好的方法。
:::warning
Linux
沒有 system("pause");
,可以使用fgetc(stdin);
代替
如果可以,寫成不需要 fgetc(stdin);
的形式更好。
:::
這次畫點的時候不需要 height-y
。
新增 指令為 ambient KIr KIg KIb
KIr KIg KIb
為環境光係數
新增 指令為 background Br Bg Bd
Br Bg Bd
為背景顏色
新增 指令為 light index Ipr Ipg Ipb Ix Iy Iz
index
為第index
支光源
Ipr Ipg Ipb
為光源顏色
Ix Iy Iz
為光原位置
:::warning 都是使用點光源 :::
結束視窗