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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

用于游戏开发和其他目的的光线投射教程

發(fā)布時(shí)間:2024/3/12 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用于游戏开发和其他目的的光线投射教程 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

用于游戲開(kāi)發(fā)和其他目的的光線投射教程

  • 前言
  • 介紹
  • 一個(gè)簡(jiǎn)短的歷史
  • 什么是光線投射
  • 用于游戲開(kāi)發(fā)的光線投射與光線追蹤
  • 光線投射的局限性
  • 光線投射第 1 步:創(chuàng)造一個(gè)世界
  • 光線投射第 2 步:定義投影屬性
  • 尋找到投影平面的距離
  • 光線投射第 3 步:尋找墻壁
    • 尋找墻壁的光線
    • 這條射線在 A、B、C、D、E 和 F 點(diǎn)與網(wǎng)格相交
  • 尋找到墻壁的距離
    • 查找到墻壁切片的距離
  • 畫(huà)墻
  • 紋理貼圖墻
  • 繪制地板
  • 繪制天花板
  • 可變高度的墻
  • 水平運(yùn)動(dòng) // 讓玩家移動(dòng)
    • A. 向前和向后移動(dòng)。
    • B、轉(zhuǎn)向。
  • 上下看
    • A. 上下看。
  • 飛行和蹲伏
  • 綜合效果
  • 陰影
  • 其他參考內(nèi)容、參考文獻(xiàn)和注釋

前言

本文檔探討了光線投射背后的基本理論,這是一種在 90 年代游戲開(kāi)發(fā)領(lǐng)域非常流行的偽 3 維渲染技術(shù)。通常,本文檔不會(huì)涉及實(shí)現(xiàn)和編碼細(xì)節(jié)。討論將主要關(guān)于概念,實(shí)現(xiàn)取決于讀者。對(duì)于普通讀者,假設(shè)了解勾股定理和高中數(shù)學(xué)知識(shí)。

本文檔寫(xiě)于 1996 年,盡管光線投射已被更新、更強(qiáng)大的技術(shù)(和硬件!)所取代,但讀者仍有望從該技術(shù)中受益。

介紹

過(guò)去幾年,個(gè)人電腦市場(chǎng)出現(xiàn)爆炸式增長(zhǎng)*。這種增長(zhǎng)部分是由對(duì)多媒體標(biāo)題的興奮和好奇產(chǎn)生的。本項(xiàng)目是嘗試在多媒體標(biāo)題開(kāi)發(fā)過(guò)程中獲得一些知識(shí)和經(jīng)驗(yàn)。具體來(lái)說(shuō),我們將仔細(xì)研究與3D多媒體游戲開(kāi)發(fā)相關(guān)的問(wèn)題。

(*本文檔寫(xiě)于 1996 年)

一個(gè)簡(jiǎn)短的歷史

1992 年,隨著一款游戲《德軍總部 3D》(iD Software)的發(fā)布,光線投射的轟動(dòng)開(kāi)始了。在 Wolfenstein 3D 中,玩家被放置在一個(gè) 3D 迷宮般的環(huán)境中,他/她必須在與多個(gè)對(duì)手作戰(zhàn)時(shí)找到出口。Wolfenstein 3D 因其快速流暢的動(dòng)畫(huà)而成為即時(shí)經(jīng)典。使這種動(dòng)畫(huà)成為可能的是一種稱為“光線投射”的創(chuàng)新 3 維渲染方法。

Wolfenstein 3D 由 Id Software 開(kāi)發(fā)和創(chuàng)建。從今以后,Id 的程序員 John Carmack 很可能是發(fā)起光線投射感覺(jué)的人(Myers 5)。

什么是光線投射

光線投射是一種技術(shù),它通過(guò)將光線從視點(diǎn)追蹤到觀看體 (LaMothe 942)來(lái)將有限形式的數(shù)據(jù)(非常簡(jiǎn)化的地圖或平面圖)轉(zhuǎn)換為 3D 投影。例如,光線投射變換在下圖中。

請(qǐng)注意,這不是光線投射的唯一應(yīng)用。光線投射也可用于渲染地形圖,例如圖 2(下圖)。要記住的重點(diǎn)是光線投射“從觀察者的眼睛向后追蹤光線到物體”。

用于游戲開(kāi)發(fā)的光線投射與光線追蹤

與光線投射一樣,光線追蹤“通過(guò)追蹤從觀察者的眼睛到場(chǎng)景中物體的假想光線來(lái)確定表面的可見(jiàn)性”(Foley 701)。

從這兩個(gè)定義來(lái)看,光線投射和光線追蹤似乎是一樣的。事實(shí)上,有些書(shū)籍交替使用這兩個(gè)術(shù)語(yǔ)。然而,從游戲程序員的角度來(lái)看,光線投射被視為光線追蹤的一種特殊實(shí)現(xiàn)(子類)。

之所以做出這種區(qū)分,是因?yàn)橐话愣?#xff0c;光線投射比光線追蹤快。這是可能的,因?yàn)楣饩€投射利用一些幾何約束來(lái)加速渲染過(guò)程。例如:墻壁總是與地板垂直(您可以在 Doom 或 Wolfenstein 3D 等游戲中看到這一點(diǎn))。如果沒(méi)有這樣的限制,光線投射將是不可行的。例如,我們不想對(duì)任意樣條進(jìn)行光線投射,因?yàn)楹茈y找到對(duì)此類形狀的幾何約束。

表 1是光線投射和光線追蹤之間的一般比較。要記住的要點(diǎn)是,由于某些“幾何約束”,在光線投射中要跟蹤的“光線數(shù)量較少”。或者,也可以說(shuō)光線投射是光線追蹤的一種特殊用途的實(shí)現(xiàn)。

表 1:光線投射和光線追蹤之間的比較
(游戲程序員/游戲開(kāi)發(fā)者的觀點(diǎn))

光線投射的局限性

光線投射很快,因?yàn)樗昧艘恍缀渭s束。在大多數(shù)情況下,墻壁始終與地板成 90 度角。(請(qǐng)注意,我們不是在談?wù)搲Ρ诤土硪幻鎵Ρ谥g的角度,而是墻壁和地板之間的角度。)

因此,光線投射游戲幾乎存在的一個(gè)限制是視點(diǎn)不能沿 Z 軸旋轉(zhuǎn)(圖 5)。如果允許這樣做,則墻壁可能會(huì)傾斜,從而失去繪制垂直切片的好處。這種無(wú)法沿 Z 軸旋轉(zhuǎn)是光線投射環(huán)境不被視為真正的 3D 環(huán)境的原因之一。

在光線投射環(huán)境中,玩家可以向前、向后、向左或向右轉(zhuǎn);但不能繞Z軸旋轉(zhuǎn)/擺動(dòng)(這種Z軸旋轉(zhuǎn)稱為傾斜)。

光線投射第 1 步:創(chuàng)造一個(gè)世界

為了說(shuō)明光線投射的過(guò)程,我們將創(chuàng)建一個(gè)具有以下幾何約束的迷宮世界:

  • 墻壁始終與地板成 90° 角。
  • 墻壁由大小相同的立方體組成。
  • 地板總是平坦的。
  • 出于我們的目的,每個(gè)立方體的大小為 64x64x64 單位。(選擇 64 是任意的,但選擇 2 的倍數(shù)會(huì)很有用;因?yàn)槲覀兛梢詫?duì)這樣的數(shù)字執(zhí)行一些算術(shù)移位運(yùn)算(移位運(yùn)算比乘法或除法快)。大小越大立方體,世界看起來(lái)會(huì)更塊,但較小的立方體會(huì)使渲染速度變慢。)
    這樣的世界如 下圖 所示。

    在繼續(xù)之前,我們將定義我們的坐標(biāo)系,以免混淆。我們使用的坐標(biāo)系如下:

    注意:任何類型的笛卡爾坐標(biāo)系都可以正常工作。但是,您必須保持一致(不要對(duì)一件事使用自上而下的坐標(biāo)系,而對(duì)其他事使用自上而下的坐標(biāo))。如果你這樣做,你可能會(huì)讓自己感到困惑——我做到了。

    光線投射第 2 步:定義投影屬性

    現(xiàn)在我們有了世界,我們需要定義一些屬性,然后才能投影和渲染世界。具體來(lái)說(shuō),我們需要知道這些屬性:

  • 玩家/觀眾的高度、玩家的視野(FOV)和玩家的位置。
  • 投影平面的尺寸。
  • 播放器與投影平面的關(guān)系。
  • 玩家應(yīng)該能夠看到他/她面前的東西。為此,我們需要定義一個(gè)視野 (FOV)。FOV 決定玩家看到他/她面前的世界的寬度(見(jiàn)下圖)。大多數(shù)人的 FOV 為 90 度或更多。然而,這個(gè)角度的 FOV 在屏幕上看起來(lái)并不好。因此,我們通過(guò)試驗(yàn)和實(shí)驗(yàn)(關(guān)于它在屏幕上看起來(lái)有多好)將 FOV 定義為 60 度。玩家的高度被定義為 32 個(gè)單位,因?yàn)榭紤]到墻壁(立方體)高 64 個(gè)單位,這是一個(gè)合理的假設(shè)。

    要將玩家置于世界中,我們需要定義玩家的 X 坐標(biāo)、玩家的 Y 坐標(biāo)以及玩家面對(duì)的角度。這三個(gè)屬性構(gòu)成了玩家的“觀點(diǎn)”。

    假設(shè)玩家以相對(duì)于世界的 45 度視角放置在網(wǎng)格坐標(biāo)(1,2)中間的某個(gè)位置,那么玩家的視角和 FOV 將如下圖 所示。(一個(gè)網(wǎng)格組成是 64 x 64 個(gè)單位。因此,我們也可以說(shuō)玩家在單位坐標(biāo)(96,160)中)。

    播放器位于網(wǎng)格坐標(biāo)(1,2)或單位坐標(biāo)(96,160)中間,視角為 45 度,視野為 60 度。

    我們需要定義一個(gè)投影平面,以便我們可以將玩家看到的東西投影到投影平面中。320 單位寬和 200 單位高的投影平面是一個(gè)不錯(cuò)的選擇,因?yàn)檫@是大多數(shù) VGA 視頻卡的分辨率。(視頻分辨率通常以像素為單位,因此可以將 1 像素視為等于 1 個(gè)單位。)
    當(dāng)玩家的視角被投影到投影平面時(shí),世界應(yīng)該看起來(lái)像下面中的場(chǎng)景。

    尋找到投影平面的距離

    通過(guò)知道視野(FOV)和投影平面的尺寸,我們可以計(jì)算出后續(xù)光線之間的角度以及玩家與投影平面之間的距離


    這是我們可以計(jì)算的(其中大部分是高中數(shù)學(xué),如果你不明白,我建議你復(fù)習(xí)三角學(xué)/勾股定理):


    所以現(xiàn)在我們知道:

    • 投影平面的尺寸 = 320 x 200 個(gè)單位
    • 投影平面的中心 = (160,100)
    • 到投影平面的距離 = 277 個(gè)單位
    • 后續(xù)光線之間的角度 = 60/320 度

    (我們偶爾會(huì)將“后續(xù)光線之間的角度”稱為“后續(xù)列之間的角度”。稍后,這個(gè)角度將用于從一列到另一列循環(huán)。玩家到投影平面之間的距離將用于縮放。)

    光線投射第 3 步:尋找墻壁

    可以將墻視為 320 條垂直線(或 320 條墻切片)的集合。

    這正是一種適用于光線投射的幾何約束形式。我們可以只跟蹤屏幕的每個(gè)垂直列,而不是為屏幕上的每個(gè)像素跟蹤一條光線。FOV 最左側(cè)的光線將投影到投影平面的第 0 列,最右側(cè)的光線將投影到投影平面的第 319 列。

    尋找墻壁的光線


    因此,為了渲染這樣的場(chǎng)景,我們可以簡(jiǎn)單地從左到右追蹤 320 條光線。這可以在循環(huán)中完成。以下說(shuō)明了這些步驟:

  • 根據(jù)視角,減去 30 度(FOV 的一半)。
  • 從第 0 列開(kāi)始:
    A. 投一縷。(術(shù)語(yǔ)“施法”有點(diǎn)令人困惑。想象一下玩家是一個(gè)可以“施放”射線而不是法術(shù)的巫師。射線只是從玩家延伸出來(lái)的“假想”線。)
    B. 跟蹤光線,直到它碰到墻壁。
  • 記錄到墻壁的距離(距離等于射線的長(zhǎng)度)。
  • 添加角度增量,使光線向右移動(dòng)(我們從圖 10 中知道,角度增量的值為 60/320 度)。
  • 對(duì)每個(gè)后續(xù)列重復(fù)步驟 2 和 3,直到投射所有 320 條光線。
  • 步驟 2A的技巧是,我們只需檢查每個(gè)網(wǎng)格,而不是檢查每個(gè)像素。這是因?yàn)閴χ荒艹霈F(xiàn)在網(wǎng)格邊界上。考慮如下面所示跟蹤的光線。要檢查這條射線是否撞到了墻壁,只需檢查 A、B、C、D、E 和 F 處的網(wǎng)格交點(diǎn)即可。

    這條射線在 A、B、C、D、E 和 F 點(diǎn)與網(wǎng)格相交

    要找到墻壁,我們需要檢查射線遇到的任何網(wǎng)格交點(diǎn);并查看網(wǎng)格上是否有墻。最好的方法是分別檢查水平和垂直交叉點(diǎn)。當(dāng)垂直或水平交叉口上有墻時(shí),檢查停止。然后比較到兩個(gè)交點(diǎn)的距離,并選擇更近的距離。以下兩幅圖說(shuō)明了此過(guò)程。


    找水平網(wǎng)格線交點(diǎn)的步驟:

  • 找到第一個(gè)交點(diǎn)的坐標(biāo)(本例中的點(diǎn) A)。
  • 找Ya。(注意:Ya只是網(wǎng)格的高度;但是,如果光線朝上,Ya將為負(fù),如果光線朝下,Ya將為正。)
  • 使用上面給出的等式找到 Xa。
  • 檢查交點(diǎn)處的網(wǎng)格。如果網(wǎng)格上有墻,停下來(lái)計(jì)算距離。
  • 如果沒(méi)有墻,則將 延伸到下一個(gè)交點(diǎn)。請(qǐng)注意,下一個(gè)交點(diǎn)的坐標(biāo) - 稱之為 (Xnew,Ynew) 是 Xnew=Xold+Xa,并且 Ynew=YOld+Ya。
  • 例如,以下是您如何獲得 A 點(diǎn):
    注意:記住笛卡爾坐標(biāo)向下增加,任何小數(shù)值都將向下舍入。

    ====== 尋找水平交點(diǎn) ====== 1.求A的坐標(biāo), 如果光線朝上 Ay = rounded_down(Py/64) * (64) - 1; 如果光線朝下Ay = rounded_down(Py/64) * (64) + 64; (圖中光線朝上,所以我們使用第一個(gè)公式 。Ay=rounded_down(224/64) * (64) - 1 = 191;現(xiàn)在,我們可以找到y(tǒng)的網(wǎng)格坐標(biāo)。但是,我們必須確定 A 是否是該線上方塊的一部分,或該線下方的塊。 在這里,我們選擇將A作為線上方塊的一部分,這就是我們從Ay中減去1的原因,所以Ay的網(wǎng)格坐標(biāo)為 191/64 = 2;Ax = Px + (Py-Ay)/tan(ALPHA);圖中,(假設(shè)ALPHA為60度),Ax=96 + (224-191)/tan(60) = 約115;Ax 的網(wǎng)格坐標(biāo)為 115/64 = 1;所以 A 在網(wǎng)格 (1,2) 處,我們可以檢查該網(wǎng)格上是否有墻。(1,2) 上沒(méi)有墻,所以光線會(huì)延伸到 C。2. 尋找 Ya如果光線朝上 Ya=-64; 如果光線朝下Ya=64; 3. 尋找 XaXa = 64/tan(60) = 364、我們可以得到C的坐標(biāo)如下:Cx=A.x+Xa=115+36=151Cy=A.y+Ya = 191-64 = 127;將每個(gè)分量除以64 將其轉(zhuǎn)化為網(wǎng)格坐標(biāo),結(jié)果為Cx = 151/64 = 2(網(wǎng)格坐標(biāo)),Cy = 127/64 = 1(網(wǎng)格坐標(biāo))所以C的網(wǎng)格坐標(biāo)為(2, 1) . (C 程序員注:記住我們總是向下取整,尤其如此,因?yàn)槟梢允褂糜乙?8 來(lái)除以 64)。 5. 網(wǎng)格 (2,1) 被選中。再次,沒(méi)有墻,所以射線延伸到 D。 6. 我們可以得到 D 的坐標(biāo)如下:Dx=C.x+Xa = 151+36 = 187Dy=C.y+Ya = 127-64 = 63;將每個(gè)分量除以64 將其轉(zhuǎn)化為網(wǎng)格坐標(biāo),結(jié)果為Dx = 187/64 = 2(網(wǎng)格坐標(biāo)),Dy = 63/64 = 0(網(wǎng)格坐標(biāo))所以D的網(wǎng)格坐標(biāo)為(2, 0) . 6. 網(wǎng)格 (2,0) 被選中。 那里有一堵墻,所以過(guò)程停止。

    (可以看到,一旦我們有了Xa和Ya的值,這個(gè)過(guò)程就很簡(jiǎn)單了,我們只要把舊的值與Xa和Ya不斷相加,并進(jìn)行移位操作,就可以找出下一個(gè)的網(wǎng)格坐標(biāo)被射線擊中的點(diǎn)。)


    尋找垂直網(wǎng)格線交點(diǎn)的步驟:

  • 找到第一個(gè)交點(diǎn)的坐標(biāo)(本例中的點(diǎn) B)。光線在圖片中朝右,所以 Bx = rounded_down(Px/64) * (64) + 64。
    如果光線朝左 Bx = rounded_down(Px/64) * (64) – 1. Ay = Py + (Px-Ax)*tan(ALPHA);
  • 找到 Xa。(注意:Xa 只是網(wǎng)格的寬度;但是,如果光線朝右,則 Xa 將為正,如果光線朝左,則 Ya 將為 負(fù)。)
  • 使用上面給出的等式找到 Ya。
  • 檢查交點(diǎn)處的網(wǎng)格。如果網(wǎng)格上有墻,停下來(lái)計(jì)算距離。
  • 如果沒(méi)有墻,則將 延伸到下一個(gè)交點(diǎn)。請(qǐng)注意,下一個(gè)交點(diǎn)的坐標(biāo) - 稱之為 (Xnew,Ynew) 只是 Xnew=Xold+Xa,而 Ynew=YOld+Ya。
  • 在圖中,首先,光線擊中點(diǎn) B。網(wǎng)格 (2,2) 被選中。(2,2) 上沒(méi)有墻,所以射線延伸到 E。網(wǎng)格 (3,0) 被檢查。那里有一堵墻,所以我們停下來(lái)計(jì)算距離。

    在此示例中,點(diǎn) D 比 E 更近。因此將繪制 D(不是 E)處的墻切片。

    尋找到墻壁的距離

    有幾種方法可以找到從視點(diǎn)(玩家)到墻壁切片的距離。它們?nèi)缦聢D所示。

    查找到墻壁切片的距離


    正弦或余弦函數(shù)的實(shí)現(xiàn)成本更低,因?yàn)樗鼈兛梢灶A(yù)先計(jì)算并放入表格中。這是可以做到的,因?yàn)?ALPHA(玩家的 POV)必須在 0 到 360 度之間,所以可能性的數(shù)量是有限的(平方根方法對(duì) x 和 y 的可能值幾乎沒(méi)有限制)。

    在畫(huà)墻之前,有一個(gè)問(wèn)題是必須要解決的。這個(gè)問(wèn)題被稱為“魚(yú)缸效應(yīng)”。魚(yú)缸效應(yīng)的發(fā)生是因?yàn)楣饩€投射實(shí)現(xiàn)將極坐標(biāo)和笛卡爾坐標(biāo)混合在一起。因此,在不直接位于觀察者面前的墻壁切片上使用上述公式將提供更長(zhǎng)的距離。這不是我們想要的,因?yàn)樗鼤?huì)導(dǎo)致如下圖所示的觀看失真。



    因此,為了消除觀看失真,從上圖 中的方程獲得的最終距離必須乘以 cos(BETA);其中 BETA 是投射的光線相對(duì)于視角的角度。在上圖中,視角 (ALPHA) 為 90 度,因?yàn)橥婕颐娉稀R驗(yàn)槲覀冇?60 度的視野,所以最左邊的光線的 BETA 是 30 度,最右邊的光線是 -30 度。

    畫(huà)墻

    在前面的步驟中,投射了 320 條光線,當(dāng)每條光線擊中墻壁時(shí),計(jì)算到該墻壁的距離。知道距離,然后可以將墻切片投影到投影平面上。為此,需要找到投影墻切片的高度。事實(shí)證明,這可以通過(guò)一個(gè)簡(jiǎn)單的公式來(lái)完成:

    實(shí)際切片高度 投影切片高度 = --------------------- * 到投影平面的距離到切片的距離

    下面的圖 解釋了該公式背后的邏輯

    墻壁縮放背后的數(shù)學(xué)


    我們的世界由立方體組成,其中每個(gè)立方體的尺寸為 64x64x64 單位, 所以墻高是64個(gè)單位。我們也已經(jīng)知道玩家到投影平面的距離(277)。因此,方程可以簡(jiǎn)化為:

    投影切片高度 = 64 / 到切片的距離 * 277

    在實(shí)際實(shí)現(xiàn)中,可以考慮以下幾點(diǎn):

    • 例如,可以預(yù)先計(jì)算 64/277,因?yàn)檫@將是一個(gè)常數(shù)值。一旦計(jì)算出來(lái),就可以在屏幕上繪制墻切片。這可以通過(guò)簡(jiǎn)單地在投影平面(屏幕)上的相應(yīng)列上繪制一條垂直線來(lái)完成。

    • 還記得數(shù)字 277 是從哪里來(lái)的嗎?這個(gè)數(shù)字實(shí)際上可以稍微偏離一點(diǎn),而不會(huì)造成太大的影響。事實(shí)上,使用255的值會(huì)節(jié)省時(shí)間,因?yàn)槌绦騿T可以使用移位運(yùn)算符來(lái)節(jié)省計(jì)算時(shí)間(右移3進(jìn)行乘法,左移進(jìn)行除法)。

    例如,假設(shè)第 200 列的光線在 330 個(gè)單位的距離處撞擊墻壁切片。切片的投影將為 64 / 330 * 277 = 54(向上取整)。
    由于投影平面的中心被定義為 100。墻切片的中間應(yīng)該出現(xiàn)在這一點(diǎn)上。因此,應(yīng)繪制墻切片的頂部位置是 100-27=73。(其中 27 是 54 的二分之一)。最后,切片的投影將類似于下圖。

    紋理貼圖墻

    為了使墻壁更具吸引力,可以使用稱為紋理映射的技術(shù)在墻壁上繪制紋理(位圖)。(紋理映射通常是指在表面上繪制位圖/紋理的技術(shù)。)對(duì)于立方體世界,我們使用大小為 64 x 64 像素的位圖。選擇這個(gè)大小是因?yàn)?64 x 64 也是我們?cè)谖覀兊氖澜缰惺褂玫牧⒎襟w面的大小。可以使用不同大小的位圖,但使用相同大小會(huì)簡(jiǎn)化紋理映射過(guò)程。

    如果我們要將紋理映射到任意多邊形上,紋理映射過(guò)程會(huì)很復(fù)雜。幸運(yùn)的是,在我們正在創(chuàng)建的光線投射世界中,紋理映射只是縮放位圖切片(一列)的問(wèn)題(參見(jiàn)下面的圖)。

    當(dāng)光線尋找墻壁交點(diǎn)時(shí),可以很容易地找到偏移量(光線相對(duì)于網(wǎng)格的位置)。然后可以使用該偏移量來(lái)確定位圖的哪一列將被繪制為墻切片。下圖說(shuō)明了查找偏移量的過(guò)程。

    繪制地板

    要繪制地板,我們可以進(jìn)行地板澆鑄(floor-casting 是指渲染地板的一種技術(shù))。但是請(qǐng)注意,在沒(méi)有紋理映射或著色的情況下執(zhí)行地板鑄造將是浪費(fèi)的。換句話說(shuō),如果地板沒(méi)有紋理或陰影(陰影將在后面探討),那么我們可以簡(jiǎn)單地用純色繪制地板,我們就完成了。牢記這一點(diǎn),讓我們探索進(jìn)行地板鑄造所需的條件。

    有幾種方法可以進(jìn)行地板澆鑄。但是,它們都使用類似的技術(shù)。該技術(shù)解釋如下。

  • 找到與地板的交點(diǎn)。
  • 確定已經(jīng)相交的地板的世界坐標(biāo)。
  • 計(jì)算玩家與地板交點(diǎn)的距離。
  • 將地板交點(diǎn)投影到投影平面上。
  • 請(qǐng)注意,沒(méi)有必要繪制所有樓層。我們應(yīng)該只繪制未被墻壁覆蓋的地板。出于這個(gè)原因,我們應(yīng)該從壁切片的底部開(kāi)始鑄造。從切片的底部,我們?nèi)缓笙蛳聮呙柰队捌矫嫔系拿總€(gè)像素(即:隨后向下投射光線)。然而,這一次,光線不是尋找與墻壁的交點(diǎn),而是尋找與地板的交點(diǎn)。


    請(qǐng)記住,我們不需要投射超出投影平面。(即:從墻片底部開(kāi)始,逐行向下投射;到達(dá)投影平面底部時(shí)停止。)

    下面的圖解釋了地板澆鑄背后的數(shù)學(xué)原理。


    重申一下,請(qǐng)?jiān)陂喿x以下步驟時(shí)查看插圖:

    • 從墻切片的底部開(kāi)始。
  • 獲取像素位置(您在進(jìn)行墻壁投射時(shí)具有此值)。
  • 從像素到觀察者的眼睛畫(huà)一條線(一條光線)。
  • 延長(zhǎng)線使其與地板相交。
  • 線與地板“相交”的點(diǎn)是紋理貼圖上被光線擊中的點(diǎn)。
  • 獲取紋理貼圖上那個(gè)點(diǎn)的像素值(參見(jiàn)下圖,了解如何做到這一點(diǎn))并將其繪制在屏幕上。
    • 重復(fù) 1-5 直到到達(dá)屏幕底部。

    繪制天花板

    要繪制天花板,可以顛倒地板澆筑過(guò)程。而不是從跟蹤光線的底部在一個(gè)壁切片的向下方向,從追蹤射線頂壁在向上方向。一旦掌握了地板鑄造背后的理論,這實(shí)際上非常簡(jiǎn)單。


    稍后,我們將解釋如何模擬仰視、俯視、飛行和蹲伏的錯(cuò)覺(jué)。如果程序員不想模擬這些,可以同時(shí)繪制地板和天花板。這是因?yàn)橥婕业难劬Φ降匕搴吞旎ò宓木嚯x相等/對(duì)稱。(地板和天花板是對(duì)稱的,因?yàn)橥婕业难劬φ迷诘匕搴吞旎ò宓闹悬c(diǎn)。)

    可變高度的墻

    到目前為止,我們世界上所有的墻都具有相同的高度。通過(guò)一些創(chuàng)新,我們實(shí)際上可以使用不同高度的墻壁。這使世界變得更有趣,如下圖所示。


    將可變高度墻概念化的最簡(jiǎn)單方法是將墻視為地板。也就是說(shuō),將墻壁想象成升高的地板。我們需要一個(gè)數(shù)組來(lái)保存每個(gè)地板網(wǎng)格的高度來(lái)完成這項(xiàng)工作。渲染場(chǎng)景的基本方法是這樣的:

  • 從投影平面的最左側(cè)列開(kāi)始。
  • 找出玩家當(dāng)前站立的地板的高度。(稱之為 CURRENT_HEIGHT。)
  • 像以前一樣投射光線并檢查交叉點(diǎn)。
  • 如果光線擊中高度與 CURRENT_HEIGHT 不同的地板,則該地板要么升高/下沉。(活動(dòng)地板只是一堵墻。)
  • 如果它被提升,那么它將是可見(jiàn)的。投影它,并渲染它。(下面的圖 30 說(shuō)明了這背后的數(shù)學(xué)原理。)
  • 如果它沉沒(méi)了,那么我們不需要對(duì)其進(jìn)行投影,因?yàn)樗鼘⒉豢梢?jiàn)。
  • 從發(fā)生高度變化的點(diǎn)開(kāi)始繪制地板,直到最后一個(gè)墻切片的頂部投影到的點(diǎn)。 (最初,最后一個(gè)墻切片的頂部將是投影平面的底部。)
  • 重復(fù)直到射線延伸通過(guò)世界地圖的限制。
  • 對(duì)所有后續(xù)列重復(fù)步驟 2 到 8。
  • 為了闡明這個(gè)過(guò)程,請(qǐng)考慮渲染下面圖中的場(chǎng)景的過(guò)程。


    首先跟蹤擊中 A 點(diǎn)(投影平面的最底行)的光線。當(dāng)光線沿投影平面向上移動(dòng)時(shí),B點(diǎn)的壁被擊中,因此繪制了切片BC。知道A點(diǎn)到B點(diǎn)沒(méi)有高度變化,就繪制了A點(diǎn)到B點(diǎn)的地板。然后射線被延伸。它檢測(cè)到 D 點(diǎn)的高度變化。因此,繪制了切片 DE。知道C點(diǎn)到D點(diǎn)沒(méi)有高度變化,繪制C點(diǎn)到D點(diǎn)的地板。然后射線再次延伸。在 F 點(diǎn),到達(dá)地圖的邊緣。由于不能再有高度變化,所以繪制了從 F 點(diǎn)到 E 點(diǎn)的地板,并重復(fù)該過(guò)程,直到渲染整個(gè)屏幕。

    使用可變高度墻的主要缺點(diǎn)是渲染過(guò)程會(huì)相當(dāng)慢。這是因?yàn)楫?dāng)最近的墻壁被擊中時(shí),光線不再停止。加快此過(guò)程的一種方法是設(shè)置可見(jiàn)距離,然后忽略超出該距離的任何內(nèi)容。

    可變高度墻背后的數(shù)學(xué)計(jì)算。

    水平運(yùn)動(dòng) // 讓玩家移動(dòng)

    玩家至少應(yīng)該能夠以三種方式移動(dòng):向前、向后和轉(zhuǎn)身。玩家的位置由坐標(biāo)和視角定義。為了允許運(yùn)動(dòng),還需要兩個(gè)屬性。它們是玩家的移動(dòng)速度,以及玩家的轉(zhuǎn)彎速度。玩家的移動(dòng)速度定義了玩家向前或向后移動(dòng)時(shí)應(yīng)該移動(dòng)多少個(gè)單位。玩家的轉(zhuǎn)彎速度(以角度衡量)定義了玩家轉(zhuǎn)彎時(shí)要增加或減少的角度。我們將討論我們?nèi)绾问褂眠@些屬性來(lái)允許運(yùn)動(dòng)。

    A. 向前和向后移動(dòng)。

    我們將玩家的移動(dòng)速度定義為 10 個(gè)單位。(通常,這可以是任何數(shù)字,但數(shù)字越大,運(yùn)動(dòng)就越不平滑。) 查找 x 和 y 位移的過(guò)程如下圖所示。如果玩家向前移動(dòng),我們將 XDisplacement 添加到當(dāng)前玩家的 X 坐標(biāo);并將 Ydisplacement 添加到當(dāng)前玩家的 Y 坐標(biāo)。如果玩家向后移動(dòng),我們將 XDisplacement 減去當(dāng)前玩家的 X 坐標(biāo);并將 Ydisplacement 減去當(dāng)前玩家的 Y 坐標(biāo)。(始終檢查世界/墻壁邊界,以免玩家走出地圖或穿過(guò)墻壁。)

    根據(jù)玩家的速度查找位移(在本例中,玩家的速度以 10 個(gè)單位表示)。

    B、轉(zhuǎn)向。

    車削過(guò)程實(shí)施起來(lái)非常簡(jiǎn)單。我們需要做的就是在當(dāng)前玩家的視角上增加或減少一個(gè)角度增量 (aI)(每當(dāng)轉(zhuǎn)彎變成一個(gè)完整的圓圈時(shí)就環(huán)繞)。同樣,較大的角度增量會(huì)導(dǎo)致運(yùn)動(dòng)看起來(lái)不太平滑。

    上下看

    可以模擬在光線投射環(huán)境中向上和向下看以及飛行和蹲伏的錯(cuò)覺(jué)。然而,請(qǐng)注意——這很重要——這里將要解釋的技巧并不總是遵循正確的三維投影理論。即:這些是技巧,它們不是進(jìn)行“真實(shí)”模擬的正確方法。

    A. 上下看。

    回想一下,投影平面是 200 個(gè)單位高。并且到此為止,我們始終將投影平面的垂直中心設(shè)置為正好在中間(即點(diǎn) y=100)。因此,任何墻切片的中點(diǎn)都將在投影點(diǎn) y=100 處繪制。事實(shí)證明,只需更改此值即可模擬向上或向下查找的效果。

    也就是說(shuō),為了模擬仰視,我們不是將垂直切片的中心放在 y=100 處,而是將其放在 y>100 的點(diǎn)上(這類似于向上移動(dòng)投影平面)。

    類似地,為了模擬向下看,我們不是將垂直切片的中心放在 y=100 處,而是將其放在 y<100 處(這類似于向下移動(dòng)投影平面)。

    為什么這個(gè)技巧有效?希望下面的插圖可以解釋它。


    如果你感到困惑,想象一下站在你身后拿著一面墻的鏡子,同時(shí)站直。當(dāng)鏡子向上或向下移動(dòng)時(shí),會(huì)顯示墻壁的不同部分。鏡子是投影平面。(在繼續(xù)之前花點(diǎn)時(shí)間想象一下。)

    飛行和蹲伏

    回想一下,玩家的高度設(shè)置為 32 個(gè)單位。這意味著玩家的眼睛(假設(shè)玩家的眼睛正好在玩家的頭頂上)正直視點(diǎn) 32 處的墻壁。由于 32 是墻壁高度的一半,因此玩家的高度為 32 會(huì)使玩家的眼睛位于地板和天花板之間的中間(見(jiàn)下圖)。

    如果我們改變這個(gè)值怎么辦?令人驚訝的是(或可能不會(huì)),墻壁會(huì)根據(jù)玩家的身高是增加還是減少而向上或向下移動(dòng)。

    因此,為了讓玩家仿佛在飛行(或跳躍),我們可以簡(jiǎn)單地增加玩家的高度。同樣,為了讓玩家好像她/他在蹲伏,我們可以降低玩家的高度。高度不應(yīng)小于 0 或大于墻壁的高度,因?yàn)檫@樣做會(huì)使玩家越過(guò)天花板或沉入地板。

    下圖顯示了此方法為何有效。


    如果您感到困惑,我們?cè)俅问褂苗R像方法來(lái)闡明其工作原理。想象一下,你站直了,在一個(gè)小房間里拿著一面鏡子。背對(duì)墻壁站立。將鏡子放在眼睛前面(即:您不必轉(zhuǎn)頭就能看到鏡子)。現(xiàn)在,想象一下如果你蹲下看看鏡子里的東西會(huì)發(fā)生什么。在鏡子里,你應(yīng)該看到墻壁的不同部分和更多的地板面積…就像本頁(yè)的第二張圖片… 希望你明白了。

    鏡子是投影平面,眼睛位置是玩家的高度。


    這種垂直運(yùn)動(dòng)方法有一個(gè)反直覺(jué)的方面,那就是:

    投影平面必須始終與玩家的眼睛垂直。(也就是說(shuō):投影平面必須始終與墻壁平行——它們不能以任何方式傾斜。)對(duì)此進(jìn)行概念化的最佳方法是想象一個(gè)人通過(guò)相機(jī)鏡頭“瞄準(zhǔn)”。人總是以 90 度角向前瞄準(zhǔn);即使他/她蹲伏或站在桌子上。

    這樣做的原因是使用這種方法時(shí),我們不能像下圖那樣傾斜投影平面;因?yàn)槿绻覀冃D(zhuǎn)投影平面以遵循“正常”的眼睛方向,那么墻壁將傾斜(不再與投影平面平行);然后渲染過(guò)程必須考慮到這一點(diǎn)。這意味著將需要更復(fù)雜的計(jì)算,并且渲染過(guò)程將變得非常緩慢。


    更真實(shí)的俯視應(yīng)該是這樣的:
    當(dāng)你往下看時(shí),你不僅會(huì)移動(dòng)眼睛,還會(huì)移動(dòng)頭部,因此視角不同(與前6張圖片相比)。本頁(yè)描述的技術(shù)使用之前的技巧來(lái)“模擬”這一點(diǎn)。

    綜合效果

    上面解釋的效果可以結(jié)合起來(lái)創(chuàng)建更有趣的動(dòng)作,如下圖所示。

    陰影

    陰影
    當(dāng)一個(gè)物體離觀察者更遠(yuǎn)時(shí),該物體應(yīng)該顯得更暗/更亮。為了實(shí)現(xiàn)這一點(diǎn),需要陰影效果。但首先,我們需要了解顏色是如何表示的。

    標(biāo)準(zhǔn)的 256 色 VGA 模式寄存器包含調(diào)色板中每種顏色的 0 到 63 之間的三個(gè)數(shù)字,稱為 RGB (RedGreenBlue) 值。例如,全紅色具有 (63,0,0) 的 RGB 分量;全綠色有 (0,63,0); 全藍(lán)有 (0,0,63)。諸如全黃之類的顏色,可以通過(guò)混合全紅和全綠使(63,63,0)為黃色來(lái)獲得。

    要更改顏色的紅色、綠色或藍(lán)色分量的亮度,必須增加或減少表示顏色分量的數(shù)字。例如,要將具有 RGB 分量 (50,10,10) 的顏色的強(qiáng)度降低一半,請(qǐng)將每個(gè)分量乘以 0.5。生成的顏色將為 (25,5,5)。

    這很簡(jiǎn)單,但是我們?nèi)绾沃涝谑裁淳嚯x上使用什么強(qiáng)度?第一個(gè)選項(xiàng)是使用一個(gè)精確的光強(qiáng)度公式,它是這樣的:

    強(qiáng)度 = (kI/(d+do))(NL)

    從游戲程序員的角度來(lái)看,這個(gè)公式太復(fù)雜了,而且速度會(huì)非常慢,所以我們甚至不會(huì)去理會(huì)它。我們的主要目標(biāo)將是制作看起來(lái)正確(或至少合理)的陰影效果。我們并不特別關(guān)心我們使用的公式是否是正確的教科書(shū)公式。

    (旁注:對(duì)于游戲編程,我傾向于同意這個(gè)原則:
    最好是擁有快速且看起來(lái)合理正確的東西;擁有完全正確但緩慢的東西。)

    因此,改為使用以下公式(Lampton 406)。

    強(qiáng)度 = 物體強(qiáng)度/距離 * 乘數(shù)

    這里,Object Intensity是程序員希望使用的強(qiáng)度(它應(yīng)該在 0 和 1 之間)。這實(shí)際上在概念上非常簡(jiǎn)單。它基本上是說(shuō)隨著物體變遠(yuǎn),物體的強(qiáng)度變小。乘數(shù)是一個(gè)數(shù)字,以防止強(qiáng)度隨距離下降到快速。這種實(shí)時(shí)計(jì)算仍然很昂貴,因此可以使用如下表所示的距離表:

    到物體的距離強(qiáng)度
    0 到 5001
    501 至 10000.75
    1001 至 15000.50

    光線投射過(guò)程在這里非常有用,因?yàn)楫?dāng)我們投射光線時(shí),我們還獲得了到要渲染的對(duì)象的距離。在實(shí)際實(shí)現(xiàn)中,我們還需要考慮可用顏色的數(shù)量。由于大多數(shù)游戲只能使用 256 種顏色,因此需要進(jìn)行一些操作以確保調(diào)色板包含正確的顏色范圍。一個(gè)可能的解決方案是使用顏色匹配算法并將結(jié)果映射到強(qiáng)度表中。渲染時(shí),我們只需從相應(yīng)的表中獲取正確的顏色值。(這非常快,因?yàn)橐粋€(gè)特定的墻切片對(duì)于它的所有像素都具有相同的強(qiáng)度。所以我們只需要在墻切片之間切換表格。)

    到物體的距離強(qiáng)度調(diào)色板映射表索引
    0 到 50011
    501 至 10000.752
    1001 至 15000.503

    通常,當(dāng)物體的強(qiáng)度接近零時(shí),物體會(huì)顯得更暗。然而,情況并非總是如此。我們可以通過(guò)改變“目標(biāo)顏色”來(lái)創(chuàng)建有趣的效果,例如霧或水下效果。例如,要?jiǎng)?chuàng)建霧效果,我們可以使調(diào)色板收斂為白色。

    其他參考內(nèi)容、參考文獻(xiàn)和注釋

    https://dev.opera.com/articles/3d-games-with-canvas-and-raycasting-part-1/

    總結(jié)

    以上是生活随笔為你收集整理的用于游戏开发和其他目的的光线投射教程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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