M3G教程:進階篇(二)燈光

來源:互聯網
上載者:User

 

燈光(Lighting)

Java.lang.Object

 +-javax.microedition.m3g.Object3D

   +-javax.microedition.m3g.Transformable

     +-javax.microedition.m3g.Node

       +-javax.microedition.m3g.Light

      在一個沒有光線的房間中,所有的東西看上去都是黑的。那麼前面的樣本中沒有光線,怎麼還能看到東西呢?頂點顏色和材質是不需要光線的,它們永遠顯示為定義好的顏色。但光線會使它們發生一些變化,可增加景深。

      光線的方向會根據對象的位置發生反射。如果您用手電筒垂直地照射您面前的鏡子,那麼光線會反射到您身上。如果鏡子是傾斜的,則光線的入射角和反射角是完全相同的。總的來說,您需要一個與照射平面相垂直的方向向量。這一向量就稱為法線向量 或簡稱為法線。M3G 會根據法線、光源位置和攝像機位置計算著色的情況。

      此外,法線是各頂點都具備的屬性,各頂點之間的像素著色既可採用插值法(PolygonMode.SHADE_SMOOTH)也可從三角形的第三個頂點處選取(PolygonMode.SHADE_FLAT)。由於立方體有 8 個頂點,支援法線的方法之一就是指定從立方體中心指向各角的向量, 7a 所示。但這樣做可能會導致立方體著色不當。有三個面的顏色可能會相同,其中有些邊成為不可見狀態,使立方體看上去缺乏稜角。這顯然更適合球體,不太適合立方體。圖 7b 展示了如何為每邊使用 4 條法線 —— 共 24 條,從而建立稜角分明的邊線。由於一個頂點只能有一條法線,所以還要複製頂點。

 

圖 7.帶有法線向量的立方體:a) 8 條法線;b) 24 條法線(每邊 4 條)

 

 

      可使用法線計算光線後,還需要告訴 M3G 您需要什麼類型的光線。光線來源於不同形式:燈泡、太陽、手電筒等等。在 M3G 中的對應術語分別為全向光、定向光線和聚光。

  • 全向光是從一個點發出的,並平均地照射各個方向。沒有燈罩的燈泡發出的就是這樣的光。
  • 定向光線向一個方向發出平行的光線。太陽離我們的距離非常遠,所以可以將其光線視為平行的。定向光線沒有位置,只有方向。
  • 手電筒或劇場中使用的聚光燈發射出的光線就是聚光。其光線呈錐形,與圓錐相交的平面上的對象會被照亮。

      在真實世界中,光線還會從對象上反射回來而將周圍照亮。如果您開啟臥室燈,就會發現即便沒有能直接照射到床底下的光線,但床下仍會被照亮。Raytracer 通過追蹤從攝像機到光源的路徑而清晰真實地展示了映像,但需要很長時間。要獲得互動式幀頻,必須滿足一個簡單的模型:環境光線。環境光線以不變的頻率從各方向照亮對象。您可以用環境光線類比前面的臥室情境,將所有對象都照亮到一定程度,從而提供了另外一個全向光源。

Light.OMNI:全向光

Light.SPOT:聚光

Light.AMBIENT:環境光線

Light.DIRECTIONAL:定向光線

 

 

材質(Material)

An Appearance component encapsulating material attributes for lighting computations. Other attributes required for lighting are defined in Light, PolygonMode and VertexBuffer.

 

The diagram below illustrates how the final, lit color is obtained for a vertex. Lighting is disabled for a submesh if it has a null Material, and enabled otherwise. If lighting is disabled, the final vertex color is taken from the associated VertexBuffer as such. If lighting is enabled, the final color is computed according to the OpenGL 1.3 lighting equation (p. 48), using the material colors specified here. Finally, if vertex color tracking is enabled, the AMBIENT and DIFFUSE material colors are replaced with the per-vertex colors or the default color obtained from the VertexBuffer.

Lighting is computed according to the OpenGL 1.3 specification, section 2.13.1, with the following exceptions:

  • the secondary color is not supported;
  • the same Material is used for both the front face and the back face;
  • vertex color tracking is limited to AMBIENT_AND_DIFFUSE;
  • for an ambient Light, the diffuse and specular intensities are zero;
  • for a directional or positional Light, the ambient intensity is zero;
  • the diffuse and specular Light intensities can not be set separately;
  • the global scene ambient color acs is not supported;

Diffuse:漫反射,反射光均勻地分散到各個方向。

Ambient:環境反射,由環境光線源反射的光線。

Emissive:放射光,一個像熾熱的物體那樣發射光線的對象。

Specular:鏡面反射,光線從有光亮平面的對象反射回來。

      環境反射僅對環境光線起作用,因此,使用全向光是無效的。漫反射材質組件會造成一種不光滑的表面,而放射光組件則製造出一種光暈效果。鏡面反射顏色組件強調了發亮的效果。此外,您還可以通過使用更多的三角形改進明暗對比的著色品質。

可以使用 Material.setVertexColorTrackingEnable() 為環境反射和漫反射使用頂點顏色,不必使用Material.setColor()

 

轉換(Transform)

      本樣本中使用了Transform將攝影機向後移動以便全方位觀看圓環,通過同樣的方式我們也可以操作任意的Object3D對象。

      您可以通過數學方式將轉換表示為矩陣操作。一個向量 —— 例如,攝像機位置 —— 乘以恰當的平移矩陣從而得到相應移動的向量。Transform 對象就表示了這樣的一個矩陣。對於絕大多數普通轉換來說,M3G 提供了 3 種便於使用的介面,隱藏了底層的數學計算:

  •  Transform.postScale(float sx, float sy, float sz):在 x、y、z 方向伸縮 3D 對象。大於 1 的值將按照給定因數擴大對象;0 和 1 之間的值將縮小對象。負值則同時執行伸縮和鏡像操作。
  • Transform.postTranslate(float tx, float ty, float tz):通過為 x、y 和 z 座標增加指定值移動 3D 對象。負值則表示向負軸方向移動對象。
  • Transform.postRotate(float angle, float ax, float ay, float az):按給定角度繞穿過(0,0,0)和(ax,ay,az)的軸旋轉對象。角度為正值,則表示若您順著正旋轉軸方向觀察,對象是按順時針旋轉的。例如,postRotate(30, 1, 0, 0) 將繞 x 軸將對象旋轉 30 度。

      所有操作名都是以 "post" 開頭的,表示當前 Transform 對象是從右邊與給定轉換矩陣相乘的 —— 矩陣操作的順序是非常重要的。如果您向右旋轉 90 度,然後走兩步,這時您所處的位置顯然與先走兩步再轉身不同。您可以在各步行指令之後調用兩個 post 方法 postRotate() 和 postTranslate(),從而獲得上面的步行指令。調用順序決定了所獲得的步行指令。由於使用的是後乘,所以您最後使用的轉換會首先應用。

      M3G 有一個 Transform 類和一個 Transformable 介面。所有快速模式的 API 均可接受 Transform 對象作為參數,用於修改其關聯的 3D 對象。另外,在保留模式下使用 Transformable 介面來轉換作為 3D 世界一部分的節點。

 

A generic 4x4 floating point matrix, representing a transformation. By default, all methods dealing with Transform objects operate on arbitrary 4x4 matrices. Any exceptions to this rule are documented explicitly at the method level.

Even though arbitrary 4x4 matrices are generally allowed, using non-invertible (singular) matrices may produce undefined results or an arithmetic exception in some situations. Specifically, if the modelview matrix of an object is non-invertible, the results of normal vector transformation and fogging are undefined for that object.

import javax.microedition.lcdui.Graphics;<br />import javax.microedition.lcdui.game.GameCanvas;<br />import javax.microedition.m3g.Appearance;<br />import javax.microedition.m3g.Camera;<br />import javax.microedition.m3g.Graphics3D;<br />import javax.microedition.m3g.IndexBuffer;<br />import javax.microedition.m3g.Light;<br />import javax.microedition.m3g.Material;<br />import javax.microedition.m3g.Mesh;<br />import javax.microedition.m3g.PolygonMode;<br />import javax.microedition.m3g.Transform;<br />import javax.microedition.m3g.TriangleStripArray;<br />import javax.microedition.m3g.VertexArray;<br />import javax.microedition.m3g.VertexBuffer;<br />import javax.microedition.m3g.World;</p><p>public class M3GCanvas extends GameCanvas implements Runnable {</p><p>public static final int FPS = 20;//每秒繪製的幀數</p><p>private Graphics3D g3d;<br />private World world;<br />private boolean runnable=true;<br />private Thread thread;<br />private Mesh mesh;<br />private Camera camera;<br />private Light light;<br />private Material material;</p><p>protected M3GCanvas() {<br />super(false);<br />setFullScreenMode(true);<br />g3d = Graphics3D.getInstance();<br />world = new World();</p><p>camera = new Camera();<br />world.addChild(camera);</p><p>float w = getWidth();<br />float h = getHeight();<br />camera.setPerspective(60.0f, w / h, 0.1f, 80f);</p><p>Transform cameraTransform = new Transform();<br />cameraTransform.postTranslate(0.0f, 15.0f, 10.0f);<br />camera.setTransform(cameraTransform);<br />camera.setOrientation(-10,1,0,0);</p><p>mesh = createCube();<br />mesh.translate(0.0f, 0.0f, -50.0f);</p><p> light=new Light();<br /> nextLightMode(light);<br /> Transform lightTransform = new Transform();<br /> lightTransform.postTranslate(0.0f, 0.0f, -4.5f);<br /> light.setOrientation(-30,1,0,0);<br />// light.setOrientation(30,0,1,0);<br /> light.setColor(0xFFFFFF);<br /> g3d.resetLights();<br /> g3d.addLight(light,lightTransform);</p><p> world.addChild(light);</p><p> world.addChild(mesh);<br /> world.setActiveCamera(camera);<br />}</p><p>public void run() {<br />Graphics g = getGraphics();<br />while (runnable) {<br />long startTime = System.currentTimeMillis();</p><p>mesh.postRotate(1.0f, 0.0f, 1.0f, 0.0f);</p><p>try {<br />//g3d.bindTarget(g);<br />g3d.bindTarget(g, true, Graphics3D.DITHER | Graphics3D.TRUE_COLOR);<br />g3d.render(world);<br />} finally {<br />g3d.releaseTarget();<br />}<br />g.setColor(0xFF00FF);<br /> g.drawString("material:"+materailInfo, 10, 10, Graphics.LEFT|Graphics.TOP);<br /> g.drawString("light:"+lightInfo, 10, 30, Graphics.LEFT|Graphics.TOP);<br />flushGraphics();</p><p>long endTime = System.currentTimeMillis();<br /> long costTime = endTime - startTime;<br /> if(costTime<1000/FPS)<br /> {<br /> try{<br /> Thread.sleep(1000/FPS-costTime);<br /> }<br /> catch(Exception e){<br /> e.printStackTrace();<br /> }<br /> }<br />}<br />System.out.println("Canvas stopped");</p><p>}</p><p>public void start()<br />{<br />thread=new Thread(this);<br />thread.start();<br />}</p><p>public void stop()<br />{<br />this.runnable=false;<br />try {<br />thread.join();<br />} catch (InterruptedException e) {<br />e.printStackTrace();<br />}<br />}</p><p>protected void keyPressed(int keyCode) {<br />switch(keyCode)<br />{<br />case KEY_NUM2:<br />nextMaterial();<br />break;<br />case KEY_NUM4:<br />nextLightMode(light);<br />break;<br />case KEY_NUM6:<br />case KEY_NUM8:<br />}<br />}</p><p>private int materialTypeIndex=0;<br />private String materailInfo=null;<br />private void nextMaterial()<br />{<br />materialTypeIndex++;<br />materialTypeIndex=(materialTypeIndex+4)%4;<br />switch(materialTypeIndex)<br />{<br />case 0:<br />material.setColor(Material.EMISSIVE, 0x00FF0000);<br />materailInfo="EMISSIVE";<br />System.out.println("use material:EMISSIVE");<br />break;<br />case 1:<br />material.setColor(Material.AMBIENT, 0x00FF0000);<br />materailInfo="AMBIENT";<br />System.out.println("use material:AMBIENT");<br />break;<br />case 2:<br />material.setColor(Material.DIFFUSE, 0x00FF0000);<br />materailInfo="DIFFUSE";<br />System.out.println("use material:DIFFUSE");<br />break;<br />case 3:<br />material.setColor(Material.SPECULAR, 0x00FF0000);<br />materailInfo="SPECULAR";<br />System.out.println("use material:SPECULAR");<br />break;<br />}</p><p>}</p><p>private int modeIndex=0;<br />private String lightInfo=null;<br />protected void nextLightMode(Light light)<br />{<br />modeIndex++;<br />modeIndex=(modeIndex+4)%4;<br />switch (modeIndex)<br />{<br /> case 0:<br /> light.setMode(Light.AMBIENT);<br /> light.setIntensity(1.0f);<br /> lightInfo="AMBIENT";<br /> System.out.println("use light mode:AMBIENT");<br /> break;<br /> case 1:<br /> light.setMode(Light.DIRECTIONAL);<br /> light.setIntensity(1.0f);<br /> lightInfo="DIRECTIONAL";<br /> System.out.println("use light mode:DIRECTIONAL");<br /> break;<br /> case 2:<br /> light.setMode(Light.OMNI);<br /> light.setIntensity(1.0f);<br /> lightInfo="OMNI";<br /> System.out.println("use light mode:OMNI");<br /> break;<br /> case 3:<br /> light.setMode(Light.SPOT);<br /> light.setSpotAngle(45.0f);<br /> light.setIntensity(2.0f);<br /> lightInfo="SPOT";<br /> System.out.println("use light mode:SPOT");<br /> break;<br /> }<br />}</p><p>private Mesh createCube(){</p><p>short[] vert = { 10, 10, 10, -10, 10, 10, 10, -10, 10, -10, -10,10, // front<br />-10, 10, -10, 10, 10, -10, -10, -10, -10, 10, -10, -10, // back<br />-10, 10, 10, -10, 10, -10, -10, -10, 10, -10, -10, -10, // left<br />10, 10, -10, 10, 10, 10, 10, -10, -10, 10, -10, 10, // right<br />10, 10, -10, -10, 10, -10, 10, 10, 10, -10, 10, 10, // top<br />10, -10, 10, -10, -10, 10, 10, -10, -10, -10, -10, -10 }; // bottom<br />VertexArray vertArray = new VertexArray(vert.length / 3, 3, 2);<br />vertArray.set(0, vert.length / 3, vert);<br />byte[] norm = { 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0,<br />-127, 0, 0, -127, 0, 0, -127, 0, 0, -127, -127, 0, 0, -127,<br />0, 0, -127, 0, 0, -127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0,<br />0, 127, 0, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0, 0, 127, 0,<br />0, -127, 0, 0, -127, 0, 0, -127, 0, 0, -127, 0 };<br />VertexArray normArray = new VertexArray(norm.length / 3, 3, 1);<br />normArray.set(0, norm.length / 3, norm);<br />int[] stripLen = { 4, 4, 4, 4, 4, 4 };<br />IndexBuffer indexBuffer = new TriangleStripArray(0, stripLen);<br />VertexBuffer vertexBuffer = new VertexBuffer();<br />vertexBuffer.setPositions(vertArray, 1.0f, null);<br />vertexBuffer.setNormals(normArray);<br />Appearance cubeAppearance = new Appearance();<br />PolygonMode pm = new PolygonMode();<br /> pm.setShading(PolygonMode.SHADE_SMOOTH);<br /> pm.setCulling(PolygonMode.CULL_NONE);</p><p> material = new Material();<br /> nextMaterial();</p><p> Appearance app = new Appearance();<br /> app.setPolygonMode(pm);<br />cubeAppearance.setMaterial(material);<br />Mesh mesh = new Mesh(vertexBuffer, indexBuffer, cubeAppearance);<br />return mesh;<br />}</p><p>}<br />

運行結果如下:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.