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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

用JavaScript玩转计算机图形学(二)基本光源

發(fā)布時(shí)間:2025/3/21 javascript 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用JavaScript玩转计算机图形学(二)基本光源 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上一篇介紹了簡(jiǎn)單的光線追蹤,湊合了臨時(shí)用的光源去渲染效果。這次將講解三種基本光源,及一些背景理論。過分簡(jiǎn)化的教材和現(xiàn)成API(OpenGL/Direct3D等)可能會(huì)做成一些錯(cuò)誤理解。在此,希望文章能簡(jiǎn)單之余,又不失背后理論。讀者明白之后,可把概念簡(jiǎn)化,或按實(shí)際情況調(diào)整。

本文代碼可在此下載(10KiB)。

讀者若喜歡本文,可按推薦按鈕以示鼓勵(lì)。如果寫得不夠清楚,或有錯(cuò)誤之處,可留言相告。

在物理上,光(light)可以視為電磁波(electromagnetic wave)或光子(photon)。在計(jì)算機(jī)圖形學(xué)的領(lǐng)域里,通常只會(huì)用到光的部份物理性質(zhì),例如假設(shè)光是直線前進(jìn)(不受因引力影響),忽略光的速度,通常不考慮衍射(diffraction)、干涉(interference )等等(好吧,也不考慮量子行為?)。因?yàn)?#xff0c;計(jì)算機(jī)圖形學(xué)不是物理學(xué),最終目標(biāo)(筆者認(rèn)為)只是要渲染視覺上美的事物,只要模擬到某個(gè)合適層次的模型,有時(shí)候還為了美觀而采用非物理/非真實(shí)的方式。

方向光源

光源(light source)放射(emit)光,而非散射(scatter)或吸收(absorb)光。

最簡(jiǎn)單的光源模型,是方向光源(directional light),又稱平行光源。這種光源假設(shè)光在無限遠(yuǎn)放射,在任何位置,放射方向都是一致的,可以模擬類似太陽的光線(雖然實(shí)際上太陽并非無限遠(yuǎn))。

方向光源的方向,通常用光向量(light vector)去表示。為方便計(jì)算,通常是單位向量,并且和光的放射方向相反

方向光源的另一個(gè)屬性,是指定其照明的量。量度光的科學(xué)叫幅射度量學(xué)(radiometry),本文暫且略過其細(xì)節(jié)。這里只用到光的其中一個(gè)量度方式,就是每秒通過每單位面積平面的光子總能量,稱為幅照度(irradiance)。

光的顏色,是由不同頻率的光波及其頻譜,在人類視覺上形成的。詳細(xì)內(nèi)容又涉及光度測(cè)定(photometry)、比色法(colorimetry)、視覺感知(visual perception)、甚至哲學(xué)等,有機(jī)會(huì)再談。這里只使用常見的紅綠藍(lán)三個(gè)顏色通道(color channel)。光源的幅照度也可以用這三通道來描述,因此,仍可用前文的Color類來描述幅照度。但注意,光的幅照度范圍是零到無限大,并不是[0,1]或[0,255]。光的"顏色"和材質(zhì)的"顏色"并非同一個(gè)概念,關(guān)于這點(diǎn),讀者可思考以下一個(gè)簡(jiǎn)單命題

客觀上,有接近白色的紙,但沒有白色的光

關(guān)于這個(gè)命題,和材質(zhì)的"顏色",將于下回分解。

陰影

一個(gè)光源的陰影(shadow),是因不透明障礙物,以致其不能到達(dá)的地方。我們可使用已有的幾何相交功能,去檢測(cè)某一位置,在方向上有否障礙物。光源追蹤方法在陰影處理上很簡(jiǎn)單,光刪化方法就復(fù)雜得多。

實(shí)現(xiàn)DirectionalLight類

在編程時(shí),需要為不同種類的光源設(shè)計(jì)一個(gè)共通接口。渲染器要從光源取得,在某個(gè)空間位置,其光向量和幅照度。在此,定義光源有一成員函數(shù)sample(scene, position),并傳回一個(gè)LightSample對(duì)象:

1 2 LightSample = function(L, EL) { this.L = L; this.EL = EL; }; LightSample.zero = new?LightSample(Vector3.zero, Color.black);

以下是方向光源的代碼,預(yù)設(shè)使用陰影:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 DirectionalLight = function(irradiance, direction) { this.irradiance = irradiance; this.direction = direction; this.shadow = true; }; DirectionalLight.prototype = { ????initialize: function() { this.L = this.direction.normalize().negate(); }, ????sample: function(scene, position) { ????????// 陰影測(cè)試 ????????if?(this.shadow) { ????????????var?shadowRay = new?Ray3(position, this.L); ????????????var?shadowResult = scene.intersect(shadowRay); ????????????if?(shadowResult.geometry) ????????????????return?LightSample.zero; ????????} ????????return?new?LightSample(this.L, this.irradiance); ????} };

渲染幅照度

sample()函數(shù)可以傳回相對(duì)光向量的幅照度,但物體表面并不一定垂直于光向量。光源越接近平面,每面積接受的能量就越少。可以想像太陽在中午是最亮的,日出日落時(shí)是最暗的。如下圖所示,平面法向量方向的面積,是光向量方向的面積的倍。

因此,設(shè)光源的光向量方向幅照度為,平面接收到的幅照度為

幅照度是能量,可以累加,所以多個(gè)光源下,平面接收到的總幅照度為

以下的簡(jiǎn)單代碼,測(cè)試一個(gè)方向光源在場(chǎng)境中的總幅照度:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 function?renderLight(canvas, scene, lights, camera) { ????// 從canvas取得imgdata和pixels,跟之前的代碼一樣 ????// ... ????scene.initialize(); ????for?(var?k in?lights) ????????lights[k].initialize(); ????camera.initialize(); ????var?i = 0; ????for?(var?y = 0; y < h; y++) { ????????var?sy = 1 - y / h; ????????for?(var?x = 0; x < w; x++) { ????????????var?sx = x / w; ????????????var?ray = camera.generateRay(sx, sy); ????????????var?result = scene.intersect(ray); ????????????if?(result.geometry) { ????????????????var?color = Color.black; ????????????????for?(var?k in?lights) { ????????????????????var?lightSample = lights[k].sample(scene, result.position); ????????????????????if?(lightSample != lightSample.zero) { ????????????????????????var?NdotL = result.normal.dot(lightSample.L); ????????????????????????// 夾角小約90度,即光源在平面的前面 ????????????????????????if?(NdotL >= 0) ????????????????????????????color = color.add(lightSample.EL.multiply(NdotL)); ????????????????????} ????????????????} ????????????????pixels[i] = color.r * 255; ????????????????pixels[i + 1] = color.g * 255; ????????????????pixels[i + 2] = color.b * 255; ????????????????pixels[i + 3] = 255; ????????????} ????????????i += 4; ????????} ????} ????ctx.putImageData(imgdata, 0, 0); }

Run

?

修改代碼試試看

  • 改變光源的顏色 (也試試超過1的值)改變光源的方向 (在DirectionalLight.initialize()裡自動(dòng)做了normalize,這輸入不需位單位向量)改變光源的幅照度?(也試試超過1的值)
  • 改變光源的方向 (在DirectionalLight.initialize()裡自動(dòng)做了normalize,這輸入不需位單位向量)

點(diǎn)光源

點(diǎn)光源/點(diǎn)光燈(point light),又稱全向光源/泛光源/泛光燈(omnidirectional light/omni light),是指一個(gè)無限小的點(diǎn),向所有光向平均地散射光。

其光向量,就是表面位置往點(diǎn)光源位置的方向:

學(xué)習(xí)物理時(shí),經(jīng)常有這種往所有方向發(fā)射的情況(例如引力、聲音等)。類比可知,接收到的能量和距離的關(guān)系,是成平方反比定律的:

當(dāng)中I為幅射強(qiáng)度(intensity, radiant intensity),當(dāng)r=1時(shí),幅射強(qiáng)度和幅照度相等。

通常稱為衰減(attenuation)系數(shù)。有時(shí)候會(huì)為各種需求,寫一些非物理正確的衰減系數(shù)。

實(shí)現(xiàn)PointLight類

以下代碼中,不直接使用normalize(),令r和其平方可以在之后分別使用,算是簡(jiǎn)單的優(yōu)化。

123456789101112131415161718192021222324252627PointLight = function(intensity, position) { this.intensity = intensity; this.position = position; this.shadow = true; };PointLight.prototype = {????initialize: function() { },????sample: function(scene, position) {????????// 計(jì)算L,但保留r和r^2,供之后使用????????var?delta = this.position.subtract(position);????????var?rr = delta.sqrLength();????????var?r = Math.sqrt(rr);????????var?L = delta.divide(r);????????// 陰影測(cè)試????????if?(this.shadow) {????????????var?shadowRay = new?Ray3(position, L);????????????var?shadowResult = scene.intersect(shadowRay);????????????// 在r以內(nèi)的相交點(diǎn)才會(huì)遮蔽光源????????????if?(shadowResult.geometry && shadowResult.distance <= r)????????????????return?LightSample.zero;????????}????????// 平方反比衰減????????var?attenuation = 1 / rr;????????// 計(jì)算幅照度????????return?new?LightSample(L, this.intensity.multiply(attenuation));????}};

Run

?

修改代碼試試看

  • 改變幅射強(qiáng)度
  • 移動(dòng)光源
  • 加入多一個(gè)點(diǎn)光源

聚光燈

現(xiàn)實(shí)中,并不存在理想的點(diǎn)光源,放射的光在不同方向是有差異的。聚光燈(spot light)是常用的一種模式,它在點(diǎn)光源的基礎(chǔ)上,加入圓錐形的范圍。聚光燈可以有不同的模型,以下采用Direct3D固定功能管道(fixed-function pipeline)用的模型做示范。

聚光燈有一個(gè)主要方向s,再設(shè)置兩個(gè)圓錐范圍,稱為內(nèi)圓錐和外圓錐,兩圓錐之間的范圍稱為半影(penumbra)。內(nèi)外圓錐的內(nèi)角分別為。聚光燈可計(jì)算一個(gè)聚光燈系數(shù),范圍為[0,1],代表某方向的放射比率。內(nèi)圓錐中系數(shù)為1(最亮),內(nèi)圓錐和外圓錐之間系數(shù)由1逐漸變成0。另外,可用另一參數(shù)p代表衰減(falloff),決定內(nèi)圓錐和外圓錐之間系數(shù)變化。方程式如下:

實(shí)現(xiàn)SpotLight類

SpotLight類只是多了那幾個(gè)參數(shù),以計(jì)算聚光燈系數(shù),最后結(jié)合到幅照度。很多參數(shù)可在initialize()里預(yù)計(jì)算,減少在sample()里重復(fù)運(yùn)算。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 SpotLight = function(intensity, position, direction, theta, phi, falloff) { ????this.intensity = intensity; ????this.position = position; ????this.direction = direction; ????this.theta = theta; ????this.phi = phi; ????this.falloff = falloff; ????this.shadow = true; }; SpotLight.prototype = { ????initialize: function() { ????????this.S = this.direction.normalize().negate(); ????????this.cosTheta = Math.cos(this.theta * Math.PI / 180 / 2); ????????this.cosPhi = Math.cos(this.phi * Math.PI / 180 / 2); ????????this.baseMultiplier = 1 / (this.cosTheta - this.cosPhi); ????}, ????sample: function(scene, position) { ????????// 計(jì)算L,但保留r和r^2,供之后使用 ????????var?delta = this.position.subtract(position); ????????var?rr = delta.sqrLength(); ????????var?r = Math.sqrt(rr); ????????var?L = delta.divide(r); ????????// 計(jì)算聚光燈因子 ????????var?spot; ????????var?SdotL = this.S.dot(L); ????????if?(SdotL >= this.cosTheta) ????????????spot = 1; ????????else?if?(SdotL <= this.cosPhi) ????????????spot = 0; ????????else ????????????spot = Math.pow((SdotL - this.cosPhi) * this.baseMultiplier, this.falloff); ????????// 陰影測(cè)試 ????????if?(this.shadow) { ????????????var?shadowRay = new?Ray3(position, L); ????????????var?shadowResult = scene.intersect(shadowRay); ????????????// 在r以內(nèi)的相交點(diǎn)才會(huì)遮蔽光源 ????????????if?(shadowResult.geometry && shadowResult.distance <= r) ????????????????return?LightSample.zero; ????????} ????????// 平方反比衰減 ????????var?attenuation = 1 / rr; ????????// 計(jì)算幅照度 ????????return?new?LightSample(L, this.intensity.multiply(attenuation * spot)); ????} };

Run

?

修改代碼試試看

  • 改變各個(gè)參數(shù)

例子

三原色

這個(gè)例子把三原色聚光燈重疊射度地板,可以看到它們的顏色混合。

Run

?

修改代碼試試看

  • 如果,幅射強(qiáng)度是負(fù)值的話,會(huì)怎么樣?(雖然未證實(shí)反光子(antiphoton)的存在,但讀者能想到圖形學(xué)上的功能么?)

很多光源

這個(gè)例子在天花加了36個(gè)點(diǎn)光源,和一個(gè)從后往前的填充用方向光源。有時(shí)候燈光師會(huì)加入填充光源(fill light),去加強(qiáng)對(duì)象的輪廓及立體感(有時(shí)候用上冷暖色的對(duì)比)。這個(gè)渲染比較慢,可能要半分鐘啊!

Run

?

修改代碼試試看

  • 把光源放在不同位置(例如接近地面)
  • 把每個(gè)光源的顏色加入差異

結(jié)語

本文簡(jiǎn)單介紹了三種基本的光源,這些光源除了應(yīng)用在光線追蹤渲染器上,也常用在光柵化渲染器中。

除這三種以外,還有一類比較高階的光源──面光源(area light)。面光源比這三種光源更真實(shí),也能完美地做到真實(shí)的柔和陰影。如果能實(shí)現(xiàn)面光源,基本上也不用特定做「光源」這種類,取而代之,可以設(shè)定某些材質(zhì)本身能發(fā)光即可。當(dāng)然,沒有免費(fèi)午餐,隨之而來的時(shí)間復(fù)雜度也增加。

有了光源,下一篇大概會(huì)開始談材質(zhì),講述光源和材質(zhì)間的互動(dòng)。

參考

  • Tomas M?ller, Eric Haines, Naty Hoffman, Real-time Rendering 3rd Edition, AK Peters 2008
  • Matt Pharr, Greg Humphreys, Physically Based Rendering, Morgan Kaufmann, 2004
from:?http://www.cnblogs.com/miloyip/archive/2010/04/02/1702768.html

總結(jié)

以上是生活随笔為你收集整理的用JavaScript玩转计算机图形学(二)基本光源的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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