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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

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

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

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

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

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

方向光源

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

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

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

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

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

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

關于這個命題,和材質的"顏色",將于下回分解。

陰影

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

實現DirectionalLight類

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

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

以下是方向光源的代碼,預設使用陰影:

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) { ????????// 陰影測試 ????????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()函數可以傳回相對光向量的幅照度,但物體表面并不一定垂直于光向量。光源越接近平面,每面積接受的能量就越少。可以想像太陽在中午是最亮的,日出日落時是最暗的。如下圖所示,平面法向量方向的面積,是光向量方向的面積的倍。

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

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

以下的簡單代碼,測試一個方向光源在場境中的總幅照度:

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()裡自動做了normalize,這輸入不需位單位向量)改變光源的幅照度?(也試試超過1的值)
  • 改變光源的方向 (在DirectionalLight.initialize()裡自動做了normalize,這輸入不需位單位向量)

點光源

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

其光向量,就是表面位置往點光源位置的方向:

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

當中I為幅射強度(intensity, radiant intensity),當r=1時,幅射強度和幅照度相等。

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

實現PointLight類

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

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

Run

?

修改代碼試試看

  • 改變幅射強度
  • 移動光源
  • 加入多一個點光源

聚光燈

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

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

實現SpotLight類

SpotLight類只是多了那幾個參數,以計算聚光燈系數,最后結合到幅照度。很多參數可在initialize()里預計算,減少在sample()里重復運算。

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) { ????????// 計算L,但保留r和r^2,供之后使用 ????????var?delta = this.position.subtract(position); ????????var?rr = delta.sqrLength(); ????????var?r = Math.sqrt(rr); ????????var?L = delta.divide(r); ????????// 計算聚光燈因子 ????????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); ????????// 陰影測試 ????????if?(this.shadow) { ????????????var?shadowRay = new?Ray3(position, L); ????????????var?shadowResult = scene.intersect(shadowRay); ????????????// 在r以內的相交點才會遮蔽光源 ????????????if?(shadowResult.geometry && shadowResult.distance <= r) ????????????????return?LightSample.zero; ????????} ????????// 平方反比衰減 ????????var?attenuation = 1 / rr; ????????// 計算幅照度 ????????return?new?LightSample(L, this.intensity.multiply(attenuation * spot)); ????} };

Run

?

修改代碼試試看

  • 改變各個參數

例子

三原色

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

Run

?

修改代碼試試看

  • 如果,幅射強度是負值的話,會怎么樣?(雖然未證實反光子(antiphoton)的存在,但讀者能想到圖形學上的功能么?)

很多光源

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

Run

?

修改代碼試試看

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

結語

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

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

有了光源,下一篇大概會開始談材質,講述光源和材質間的互動。

參考

  • 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

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。