日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【GAMES101】作业2--三角形光栅化

發(fā)布時間:2023/12/16 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【GAMES101】作业2--三角形光栅化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 作業(yè)要求
    • 額外說明
    • 評分
  • 解答
    • 1 復制粘貼上節(jié)課的get_projection_matrix
    • 2 判斷像素點是否在三角形內(nèi)部insideTriangle
      • 對應知識
      • 對應代碼
    • 3 光柵化
      • 步驟概覽
      • 1. 找到Bounding Box
      • 2. 在Bounding box 內(nèi)遍歷所有元素,判斷是否在三角形內(nèi)部
      • 3. 根據(jù)已有代碼來得到深度值,也就是z_interpolated
      • 4. 如果當前位置深度比depth_buf更小,則更新顏色值并保存深度值
  • 代碼總結(jié)
  • 結(jié)果

前言

本文為GAMES101現(xiàn)代計算機圖形學入門 的學習筆記系列。

我們的系列筆記將分為兩部分:

  • 課堂筆記
  • 作業(yè)
  • 原課程為2020年2月閆令琪所教授的 GAMES101 現(xiàn)代計算機圖形學入門

    課程主頁:https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html
    (幻燈片和課程錄像均在此處)

    課程共計22節(jié)。作業(yè)共計8次。

    針對人群:計算機圖形學入門新手

    教材
    Steve Marschner and Peter Shirley的"Fundamentals of Computer Graphics"
    第三版或更新版本。目前無官方中文版。
    民間翻譯:https://www.stubbornhuang.com/1812/

    筆記目錄


    2022-6-9

    作業(yè)要求

    在上次作業(yè)中,雖然我們在屏幕上畫出一個線框三角形,但這看起來并不是那么的有趣。所以這一次我們繼續(xù)推進一步——在屏幕上畫出一個實心三角形,換言之,柵格化一個三角形。上一次作業(yè)中,在視口變化之后,我們調(diào)用了函數(shù)rasterize_wireframe(const Triangle& t)。但這一次,你需要自己填寫并調(diào)用函數(shù) rasterize_triangle(const Triangle& t)。

    該函數(shù)的內(nèi)部工作流程如下:

  • 創(chuàng)建三角形的 2 維 bounding box。
  • 遍歷此 bounding box 內(nèi)的所有像素(使用其整數(shù)索引)。然后,使用像素中心的屏幕空間坐標來檢查中心點是否在三角形內(nèi)。
  • 如果在內(nèi)部,則將其位置處的插值深度值 (interpolated depth value) 與深度緩沖區(qū) (depth buffer) 中的相應值進行比較。
  • 如果當前點更靠近相機,請設置像素顏色并更新深度緩沖區(qū) (depth buffer)。
  • 你需要修改的函數(shù)如下:

    • rasterize_triangle(): 執(zhí)行三角形柵格化算法
    • static bool insideTriangle(): 測試點是否在三角形內(nèi)。你可以修改此函數(shù)的定義,這意味著,你可以按照自己的方式更新返回類型或函數(shù)參數(shù)。

    額外說明

    因為我們只知道三角形三個頂點處的深度值,所以對于三角形內(nèi)部的像素,我們需要用插值的方法得到其深度值。我們已經(jīng)為你處理好了這一部分,因為有關這方面的內(nèi)容尚未在課程中涉及。插值的深度值被儲存在量 z_interpolated中。

    請注意我們是如何初始化 depth buffer 和注意 z values 的符號。為了方便同學們寫代碼,我們將 z 進行了反轉(zhuǎn),保證都是正數(shù),并且越大表示離視點越遠。

    在此次作業(yè)中,你無需處理旋轉(zhuǎn)變換,只需為模型變換返回一個單位矩陣。最后,我們提供了兩個 hard-coded 三角形來測試你的實現(xiàn),如果程序?qū)崿F(xiàn)正確,你將看到如下所示的輸出圖像:

    在你自己的計算機或虛擬機上下載并使用我們更新的框架代碼。你會注意到,在 main.cpp 下的 get_projection_matrix() 函數(shù)是空的。請復制粘貼你在第一次作業(yè)中的實現(xiàn)來填充該函數(shù)。

    評分

    • [5 分] 正確地提交所有必須的文件,且代碼能夠編譯運行。
    • [20 分] 正確實現(xiàn)三角形柵格化算法。
    • [10 分] 正確測試點是否在三角形內(nèi)。
    • [10 分] 正確實現(xiàn) z-buffer 算法, 將三角形按順序畫在屏幕上。
    • [提高項 5 分] 用 super-sampling 處理 Anti-aliasing : 你可能會注意到,當我們放大圖像時,圖像邊緣會有鋸齒感。我們可以用 super-sampling來解決這個問題,即對每個像素進行 2 * 2 采樣,并比較前后的結(jié)果 (這里并不需要考慮像素與像素間的樣本復用)。需要注意的點有,對于像素內(nèi)的每一個樣本都需要維護它自己的深度值,即每一個像素都需要維護一個 sample list。最后,如果你實現(xiàn)正確的話,你得到的三角形不應該有不正常的黑邊。

    解答

    我們需要填寫的兩個函數(shù)都在rasterize.cpp當中。并且我們需要復制粘貼上一次作業(yè)寫的get_projection_matrix。

    我們首先需要寫一個判斷像素點是否在三角形內(nèi)部的算法。這個函數(shù)的返回值就是一個bool變量。這個函數(shù)名為static bool insideTriangle()

    rasterize_triangle()函數(shù)是用來三角形柵格化的。它分為以下三步:

  • 創(chuàng)建三角形的boundingBox。
  • 在boundingBox內(nèi)判斷像素點坐標是否在三角形內(nèi)(利用上面寫的insideTriangle函數(shù))
  • 通過深度插值,從三角形三個頂點的深度值找到每個像素的深度值。(這一點代碼已經(jīng)被給出)
  • 如果當前位置深度比depth_buf(類rasterizer的一個成員)更小,則更新顏色值。
  • 1 復制粘貼上節(jié)課的get_projection_matrix

    我們先復制粘貼get_projection_matrix
    如下

    Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,float zNear, float zFar) {// Students will implement this functionEigen::Matrix4f projection = Eigen::Matrix4f::Identity();// TODO: Implement this function// Create the projection matrix for the given parameters.// Then return it.// 1. 透視投影轉(zhuǎn)為正交投影矩陣float n = zNear;float f = zFar;Eigen::Matrix4f M_persp2Ortho;M_persp2Ortho << n, 0, 0, 0,0, n, 0, 0,0, 0, n + f, -n * f,0, 0, 1, 0;// 2. 正交投影轉(zhuǎn)換到正則立方體float fov = eye_fov * MY_PI / 180.0;float t = -n * tan(fov / 2.);float b = -t;float r = aspect_ratio * t;float l = -r;Eigen::Matrix4f M_ortho, trans, scale;trans << 1, 0, 0, -(r+l)/2,0, 1, 0, -(t+b)/2,0, 0, 1, -(n+f)/2,0, 0, 0, 1;scale << 2/(r-l), 0, 0, 0,0, 2/(t-b), 0, 0,0, 0, 2/(n-f), 0,0, 0, 0, 1;M_ortho = scale * trans;projection = M_ortho * M_persp2Ortho ;return projection; }

    2 判斷像素點是否在三角形內(nèi)部insideTriangle

    題目中給出的API如下

    static bool insideTriangle(int x, int y, const Vector3f* _v) { // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2] }

    我們接受一個x,接受一個y,這代表了當前點的像素編號(整數(shù))。接受一個向量引用 _v,這代表了三角形的三個頂點坐標。

    對應知識

    課堂筆記2–向量與線性代數(shù)

    對應代碼

    static bool insideTriangle(float x, float y, const Vector3f* _v) { // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]const Eigen::Vector2f P(x, y);const Eigen::Vector2f A = _v[0].head(2), B = _v[1].head(2), C = _v[2].head(2);const Eigen::Vector2f AP = P - A;const Eigen::Vector2f BP = P - B;const Eigen::Vector2f CP = P - C;const Eigen::Vector2f AB = B - A;const Eigen::Vector2f BC = C - B;const Eigen::Vector2f CA = A - C;float eq1 = AB[0] * AP[1] - AB[1] * AP[0];float eq2 = BC[0] * BP[1] - BC[1] * BP[0]; float eq3 = CA[0] * CP[1] - CA[1] * CP[0];if( eq1 > 0 && eq2 > 0 && eq3>0)return true;else if(eq1 < 0 && eq2<0 && eq3<0)return true;else return false; }

    注意以下幾點:

  • 應該注意const Vector3f* _v這個參數(shù)
    這個參數(shù)是一個Vector3f指針,實際上代表的是一個數(shù)組。數(shù)組中的每個元素是一個Vector3f類型,
    也就是代表了一個頂點的坐標。每個點用_v[0],_v[1],_v[2]表示。而每個_v[0]就是一個Vector3f類型的點。整個數(shù)組代表了多個點。這里一般認為傳入的是三個點,即一個三角形。

  • 注意我們做的是2D模擬,因此只是傳入了x、y坐標。而Vector3f是一個3D的坐標,因此我們只使用它的前兩個坐標。在Eigen當中就是使用.head(2)函數(shù)

  • 二維的向量對于Eigen來說是無法做叉乘的。Eigen只支持三維叉乘。所以我們要手動寫叉乘。
    a×b=(a1,a2)T×(b1,b2)T=a1b2?a2b1a\times b = (a1, a2)^T \times (b1, b2)^T \\ = a1b2 - a2 b1 a×b=(a1,a2)T×(b1,b2)T=a1b2?a2b1
    請注意,上面的寫法是不嚴謹?shù)?#xff0c;因為叉乘是不改變張量的階的。向量叉乘,得到的應該還是向量。所以這里只是表示了大小,而沒有表示方向。實際上,方向是指向垂直于紙面(向外或者內(nèi))。如果想要嚴謹?shù)貙懗鰜?#xff0c;只需要把第三個分量寫為0,然后按照3D向量叉乘公式(也就是行列式),如下,寫出來即可。你會發(fā)現(xiàn)只剩下了k基矢的項。

  • 3 光柵化

    步驟概覽

    上文說到

    rasterize_triangle()函數(shù)是用來三角形柵格化的。它分為以下4步:

  • 創(chuàng)建三角形的boundingBox。
  • 在boundingBox內(nèi)判斷像素點坐標是否在三角形內(nèi)(利用上面寫的insideTriangle函數(shù))
  • 通過深度插值,從三角形三個頂點的深度值找到每個像素的深度值。(這一點代碼已經(jīng)被給出)
  • 如果當前位置深度比depth_buf(類rasterizer的一個成員)更小,則更新顏色值。
  • 我們依次來做這四步(其中第三步已經(jīng)被寫好了)

    1. 找到Bounding Box

    怎么找呢?其實就是找三角形的x坐標和y坐標的最大最小值而已!
    比如下面這張圖
    我們的bounding box就是所在范圍的9個格子。

    對應的代碼就是

    //1. 找到Bounding Boxfloat xmin = std::min(std::min(v[0].x(), v[1].x()), v[2].x());float ymin = std::min(std::min(v[0].y(), v[1].y()), v[2].y());float xmax = std::max(std::max(v[0].x(), v[1].x()), v[2].x());float ymax = std::max(std::max(v[0].y(), v[1].y()), v[2].y());

    注意:根據(jù)課堂筆記6–光柵化(深度測試與抗鋸齒),其實坐標值和整數(shù)編號值只是差了0.5

    我們找bounding box,是為了接下來做for循環(huán)遍歷。所以假如找到的是float類型的,不那么方便循環(huán)。因為坐標值和整數(shù)編號值只是差了0.5,所以我們就往外拓展一點點boundingbox。如下面的代碼所示。

    //我們將bounding box稍微擴大一點,得到整數(shù)值,方便循環(huán)xmin = (int)std::floor(xmin);xmax = (int)std::ceil(xmax);ymin = (int)std::floor(ymin);ymax = (int)std::ceil(ymax);

    2. 在Bounding box 內(nèi)遍歷所有元素,判斷是否在三角形內(nèi)部

    這一步很簡單,就是寫兩個for循環(huán),然后再利用第一步寫了的insideTriangle函數(shù)來判斷

    //2. 在Bounding box 內(nèi)遍歷所有元素,判斷是否在三角形內(nèi)部for (int x = xmin; x <= xmax; x++){for (int y = ymin ; y <= ymax; y++){//像素的坐標值只是比整數(shù)編號值大0.5而已。if (insideTriangle(x + 0.5, y + 0.5, t.v)){

    我們這里唯一要注意的,就是insideTriangle函數(shù)的使用方法

    這個函數(shù)接收三個參數(shù),前兩個就是坐標值的x,y而已。剛才已經(jīng)說了,坐標值就是像素編號+0.5而已。

    第三個參數(shù)實際上是一個數(shù)組,這個數(shù)組的每個元素都是一個3維Vector3f類型的點。

    我們現(xiàn)在所擁有的參數(shù),是const Triangle& t,這是一個三角形,是代碼框架自定義的類型。它的類型定義位于Triangle.hpp

    class Triangle{public:Vector3f v[3]; /*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*/... };

    我們只要看它的第一個成員變量,叫做v,它恰好是一個數(shù)組,數(shù)組的每個元素是個Vector3f的點。這就對應上我們需要的參數(shù)。

    3. 根據(jù)已有代碼來得到深度值,也就是z_interpolated

    直接取消注釋已有的代碼即可

    //3.根據(jù)已有代碼來得到深度值,也就是z_interpolated // If so, use the following code to get the interpolated z value.auto [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();z_interpolated *= w_reciprocal;

    這一步的目的,就是把三角形三個頂點的深度值插值成三角形內(nèi)每個像素點的深度值

    4. 如果當前位置深度比depth_buf更小,則更新顏色值并保存深度值

    很簡單,假如該點當前的深度值比buffer中的更小,就更新顏色,并且保存新的深度值。

    這就是z-buffer算法。這個算法很簡單,其實就是尋找最小值而已。找到了最小值,就覆蓋原來的最小值,并且更新顏色。depth_buf保存的就是原來的最小值。

    //4. 如果當前位置深度比depth_buf(類rasterizer的一個成員)更小,則更新顏色值。if (z_interpolated < depth_buf[get_index(x, y)]){//TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.set_pixel(Vector3f(x, y, z_interpolated), t.getColor());depth_buf[get_index(x, y)] = z_interpolated;}

    我們這里要注意2點:

  • depth_buf的使用方法。
    depth_buf是自定義的Triangle類型的一個成員變量。
  • std::vector<float> depth_buf;

    它就是個一維浮點數(shù)數(shù)組而已。使用方法就是用數(shù)組下標。但是這里要注意了:我們的像素編號是兩個(x和y),所以要先把它轉(zhuǎn)換成一個。轉(zhuǎn)換的方法通俗易懂,就是一行行地排排坐,排完一行再排一行。從左到右,從上到下。

    如圖所示

    甚至代碼框架里面已經(jīng)給你寫好了轉(zhuǎn)換的函數(shù)

    int rst::rasterizer::get_index(int x, int y) {return (height-1-y)*width + x; }

    這里的height和width也是整數(shù)。實際上,由于OpenCV也許是從左上到右下排列的,所以它可能用的是(height-1-y)。這里就是把數(shù)字轉(zhuǎn)換一下。

    但是這些都不用我們操心,我們只要使用get_index函數(shù)就行了。

  • set_pixel的使用方法
  • void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color)

    它接收兩個參數(shù),第一個就是點坐標,第二個就是RGB顏色。

    第一個參數(shù)我們就給當前像素點的坐標即可,Vector3f(x, y, z_interpolated)
    第二個參數(shù),我們就給t.getColor()

    class Triangle{public:Vector3f v[3]; /*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*//*Per vertex values*/Vector3f color[3]; //color at each vertex;...Vector3f getColor() const { return color[0]*255; } // Only one color per triangle.... };

    這個函數(shù)就是Triangle所需要繪制的顏色值。

    代碼總結(jié)

    第一個函數(shù)

    static bool insideTriangle(float x, float y, const Vector3f* _v) { // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]const Eigen::Vector2f P(x, y);const Eigen::Vector2f A = _v[0].head(2), B = _v[1].head(2), C = _v[2].head(2);const Eigen::Vector2f AP = P - A;const Eigen::Vector2f BP = P - B;const Eigen::Vector2f CP = P - C;const Eigen::Vector2f AB = B - A;const Eigen::Vector2f BC = C - B;const Eigen::Vector2f CA = A - C;float eq1 = AB[0] * AP[1] - AB[1] * AP[0];float eq2 = BC[0] * BP[1] - BC[1] * BP[0]; float eq3 = CA[0] * CP[1] - CA[1] * CP[0];if( eq1 > 0 && eq2 > 0 && eq3>0)return true;else if(eq1 < 0 && eq2<0 && eq3<0)return true;else return false; }

    第二個函數(shù)

    //Screen space rasterization void rst::rasterizer::rasterize_triangle(const Triangle& t) {auto v = t.toVector4();// TODO : Find out the bounding box of current triangle.// iterate through the pixel and find if the current pixel is inside the triangle//1. 找到Bounding Boxfloat xmin = std::min(std::min(v[0].x(), v[1].x()), v[2].x());float ymin = std::min(std::min(v[0].y(), v[1].y()), v[2].y());float xmax = std::max(std::max(v[0].x(), v[1].x()), v[2].x());float ymax = std::max(std::max(v[0].y(), v[1].y()), v[2].y());//我們將bounding box稍微擴大一點,得到整數(shù)值,方便循環(huán)xmin = (int)std::floor(xmin);xmax = (int)std::ceil(xmax);ymin = (int)std::floor(ymin);ymax = (int)std::ceil(ymax);//2. 在Bounding box 內(nèi)遍歷所有元素,判斷是否在三角形內(nèi)部for (int x = xmin; x <= xmax; x++){for (int y = ymin ; y <= ymax; y++){//像素的坐標值只是比整數(shù)編號值大0.5而已。if (insideTriangle(x + 0.5, y + 0.5, t.v)){//3.根據(jù)已有代碼來得到深度值,也就是z_interpolated // If so, use the following code to get the interpolated z value.auto [alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);float w_reciprocal = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();z_interpolated *= w_reciprocal;//4. 如果當前位置深度比depth_buf(類rasterizer的一個成員)更小,則更新顏色值。if (z_interpolated < depth_buf[get_index(x, y)]){//TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.set_pixel(Vector3f(x, y, z_interpolated), t.getColor());depth_buf[get_index(x, y)] = z_interpolated;}}}} }

    結(jié)果

    總結(jié)

    以上是生活随笔為你收集整理的【GAMES101】作业2--三角形光栅化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。