- 浏览: 809759 次
- 性别:
- 来自: 广州
最新评论
-
mixture:
语句int num1, num2;的频度为1;语句i=0;的频 ...
算法时间复杂度的计算 [整理] -
zxjlwt:
学习了。http://surenpi.com
[问题解决]Error: ShouldNotReachHere() [整理] -
Animal:
谢谢 楼主 好东西
算法时间复杂度的计算 [整理] -
univasity:
gaidandan 写道缓存失败,,模拟器上可以缓存,同样代码 ...
[开发总结]WebView使用中遇到的一些问题&解决 -
blucelee2:
那么麻烦干吗,而且这种方法会导致,当拉太小的时候样式会丢掉,整 ...
[SWT]SashForm中固定单侧大小(&实现面板隐藏)
<!-- 整理收集自网络,收藏以便日后查阅 -->
Compliments of Redikod:
3D programming tutorial part five: Heightmap terrain rendering using M3G
Building on the previous four installments of his getting-started tutorial series "3D programming for mobile devices using M3G (JSR 184)", Mikael Baros, Senior Programmer at Redikod, now guides you through the basics of heightmaps and terrain rendering. Below you can download the source code and application package zip files for part five, as well as link to the first four installments of the tutorial. |
|
Part five: Heightmap terrain rendering using M3G Source Code (Java classes and resources)>> Application Package (JAR/JAD)>>
So, over to Mikael for part five.
Introduction |
|
Welcome to the fifth part of this M3G tutorial series. Today I'll show you a very simple technique that is used in almost all 3D games (in one form or the other). The heightmap. By using heightmaps, designers/developers can easily create natural terrain (perhaps even by using a perlin noise generator, but not required) in almost no time at all. The beauty of heightmaps is that it takes a complex concept, such as a beautiful and realistic 3D-terrain, and simplifies this problem to an easy 2D-image. As always, check here if you ever get lost: First of all, and probably most importantly, the dedicated Mobile Java 3D web section on Sony Ericsson Developer World. Second, if you ever get stuck, go to the Sony Ericsson Mobile Java 3D forum . For everything else, use the Sony Ericsson World Developer web portal , there you will find the answers to your questions and more. |
|
What you should know |
3D terrain As you can see, the above terrain is described by three areas of greater height (the three gray hills) and the rest is a deep gorge, that is filled with water. Again, nothing but variations in height. |
Heightmaps
This is where heightmaps come in. They are very elegant
solutions to storing variations in height and making surfaces smooth.
Now let's look at this image before I start revealing anything.
This is a grayscale image. Nothing fancy. It looks like a donut with a white speck in the middle. So what is a grayscale image? Well simply put, it is a collection of pixels where each pixel goes from 0 to 255 on the grayscale where 0 is black and 255 completely white. Right? Does this sound familiar? What if you could use each pixel to determine height then? If a black pixel (value 0) could be the lowest height and a white pixel (value 255) the highest you'd have a map that depicts height, a heightmap! Another great thing about this is that since pixels go from 0 to 255 you get automatic interpolation of the terrain (thus creating smooth terrain) if you just blur your image. |
So, all you need to do is to open your favorite graphics program, draw some white stuff with a brush and you have a heightmap. This all sounds easy, sure, but how do we actually convert this to a mesh that we can render? Quads |
The image above represents a quad that consists of two triangles. As you can see, the quad has four endpoints that are disjointed since we are using two triangles to represent it. These four corners can be given different heights and thus we have the beginning of describing heights in a 3D world. One quad is far from enough to describe an entire terrain however, we'll need a LOT of them if we want our terrain to look the least bit realistic. We'll get to that later, let's first see how we create a quad in code. We'll make it a x-z plane with variable y-coordinates, thus having varying height. Let's introduce a new method to the MeshFactory class we created in the previous tutorials and call it createQuad. All this method needs to know is the different heights at the different corners of the quad, and the culling flags. Here's the first piece of the method:
public static Mesh createQuad(short[] heights, int cullFlags) // Create the model's vertex colors // Create the appearance Here is some standard Appearance stuff, however I wanted you to see
that now we're using smooth shading, which means that colors of the
vertrices will be interpolated over the whole surface, creating a smooth
appearance. Why we need this will become clearer later. All that's left
now is to create the Mesh, which is fairly straightforward:
{
// The vertrices of the quad
short[] vertrices = {-255, heights[0], -255,
255, heights[1], -255,
255, heights[2], 255,
-255, heights[3], 255};
VertexArray colorArray = new VertexArray(color.length/3, 3, 1);
colorArray.set(0, color.length / 3, color);
// Compose a VertexBuffer out of the previous vertrices and texture coordinates
VertexBuffer vertexBuffer = new VertexBuffer();
vertexBuffer.setPositions(vertexArray, 1.0f, null);
vertexBuffer.setColors(colorArray);
// Create indices and face lengths
int indices[] = new int[] {0, 1, 3, 2};
int[] stripLengths = new int[] {4};
// Create the model's triangles
triangles = new TriangleStripArray(indices, stripLengths);
Appearance appearance = new Appearance();
PolygonMode pm = new PolygonMode();
pm.setCulling(cullFlags);
pm.setPerspectiveCorrectionEnable(true);
pm.setShading(PolygonMode.SHADE_SMOOTH);
appearance.setPolygonMode(pm);
// Finally create the Mesh I have drawn a white grid over the previous heightmap. If
you look at each piece of the grid, you can see that the rectangular
grid sector is another heightmap but smaller. If we create a very
high-resolution grid, you'll probably realize that the grid sectors
become very small and thus very easy to approximate with a single quad.
To put it simply; in order to approximate a heightmap, we split it into
many very small parts which each represent a quad. How do we create a
quad? Easy, here are the steps necessary:
Mesh mesh = new Mesh(vertexBuffer, triangles, appearance);
So, it's really simple to create the quads from a heightmap. After creation, you just render these quads, one after another and you have your heightmap. Now, there are some things you should know. As the resolution of the heightmap grid increases, so does the smoothness of the terrain, as you use more quads to represent the terrain. However you are also drastically increasing the memory footprint and increasing the number of polygons that the GPU has to push. This is a trade-off that needs to be done on every mobile phone depending on available memory, GPU power, etc.
Implementation
Let's see how to implement a heightmap in M3G. We already have a
method that creates Quads with varying heights so all we need is to:
1. Load a heightmap
2. Create a new array that is scaled proportionally to the grid size
3. Read pixels from heightmap and store in the new array
4. Use said array to generate Quads with varying heights
It's a simple four-step procedure. Let's begin by inspecting the private members of the HeightMap class:
// The actual heightmap containing the Y-coords of our triangles public HeightMap(String imageName, float resolution, int waterLevel) throws IOException Next we load the image that we supplied as a constructor parameter.
However, there are some other interesting things done in the loadImage
method I'd like you to see. Here is the code: // Load actual image Next method in the constructor body is the createQuads. This is a
very straightforward method that takes the generated heightMap array and
creates quads from it. Let's look at its guts: private void createQuads() I'll leave it up to you to check the createWater method. It should be
something you know by heart at this point. We just use the
MeshFactory.createPlane method to create a large plane textured with a
watery texture. Rendering public void render(Graphics3D g3d, Transform t) Putting it all together
1. Load a HeightMap from an existant greyscale image Sounds easy? That's because it is. Let's take a look at the code that loads a HeightMap: private void createScene() Another very important thing to keep in mind is that the HeightMap in
this tutorial is rendered without any culling at all. This is needed,
especially on large terrains. However to keep clarity in the code I have
chosen to remove any kind of space partitioning or software culling.
You can see it as an exercise to only send meshes to the renderer that
are visible (that is, not meshes that are too far away, or behind the
camera). Finally, what's the code for rendering the HeightMap? Here is the main draw method: // Get the Graphics3D context Conclusion
Let your imagination go wild, put it into the MIDlet and cruise through your landscape.
private short[] heightMap;
private int[] data;
private int imgw, imgh;
// Map dimensions
private int mapWidth;
private int mapHeight;
// Actual quads
private Mesh[][] map;
// Water
private Mesh water;
// Local transform used for internal calculations
private Transform localTransform = new Transform();
{
// Check for invalid resolution values
if(resolution <= 0.0001f || resolution > 1.0f)
throw new IllegalArgumentException("Resolution too small or too large");
// Load image and allocate the internal array
loadImage(imageName, resolution);
// Create quads
createQuads();
// Create the water
createWater(waterLevel);
}
Image img = Image.createImage(path);
// Allocate temporary memory to store pixels
data = new int[img.getWidth() * img.getHeight()];
// Get its rgb values
img.getRGB(data, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
imgw = img.getWidth();
imgh = img.getHeight();
// Clear image
img = null;
System.gc();
// Calculate new width and height
mapWidth = (int)(res * imgw);
mapHeight = (int)(res * imgh);
// Allocate heightmap
heightMap = new short[mapWidth * mapHeight];
// Calculate height and width offset into image
int xoff = imgw / mapWidth;
int yoff = imgh / mapHeight;
// Set height values
for(int y = 0; y < mapHeight; y++)
{
for(int x = 0; x < mapWidth; x++)
{
heightMap[x + y * mapWidth] = (short)((data[x * xoff + y * yoff * imgw] & 0x000000ff) * 10);
}
}
// Clear data
data = null;
img = null;
System.gc();
{
map = new Mesh[mapWidth][mapHeight];
short[] heights = new short[4];
for(int x = 0; x < (mapWidth - 1); x++)
{
for(int y = 0; y < (mapHeight - 1); y++)
{
// Set heights
setQuadHeights(heights, x, y, mapWidth);
// Create mesh
map[x][y] = MeshFactory.createQuad(heights, PolygonMode.CULL_NONE);
}
}
}
How do we render the Quads we've generated? You should know the
answer to this question, but let's go through the render method of the
HeightMap class anyway. Here it is:
{
for(int x = 0; x < map.length - 1; x++)
{
for(int y = 0; y < map[x].length - 1; y++)
{
localTransform.setIdentity();
localTransform.postTranslate(x * 5.0f, 0.0f, (mapHeight - y) * -5.0f);
localTransform.postMultiply(t);
g3d.render(map[x][y], localTransform);
}
}
localTransform.setIdentity();
localTransform.postScale(255, 255, 255);
localTransform.postRotate(-90, 1.0f, 0.0f, 0.0f);
g3d.render(water, localTransform);
}
Now to use the very nifty HeightMap class we need to do the following:
2. Render it
{
try
{
// We're using a pretty high resolution. If you want to test this on an actual
// handheld, try using a lower resolution, such as 0.20 or 0.10
hm = new HeightMap("/res/heightmap4.png", 0.30f, 40);
t.postTranslate(0.0f, -2.0f, -5.0f);
t.postScale(0.01f, 0.01f, 0.01f);
camTrans.postTranslate(0.0f, 5.0f, 0.0f);
//camTrans.postTranslate(0.0f, 5.0f, 2.0f);
}
catch(Exception e)
{
System.out.println("Heightmap error: " + e.getMessage());
e.printStackTrace();
TutorialMidlet.die();
}
}
g3d = Graphics3D.getInstance();
// First bind the graphics object. We use our pre-defined rendering hints.
g3d.bindTarget(g, true, RENDERING_HINTS);
// Clear background
g3d.clear(back);
// Bind camera at fixed position in origo
g3d.setCamera(cam, camTrans);
// Render everything
hm.render(g3d, t);
// Check controls for camera movement
if(key[UP])
{
camTrans.postTranslate(0.0f, 1.0f, 0.0f);
}
if(key[DOWN])
{
camTrans.postTranslate(0.0f, -1.0f, 0.0f);
}
if(key[LEFT])
{
camTrans.postRotate(5, 0.0f, 1.0f, 0.0f);
}
if(key[RIGHT])
{
camTrans.postRotate(-5, 0.0f, 1.0f, 0.0f);
}
// Fly forward
if(key[FIRE])
camTrans.postTranslate(0.0f, 0.0f, -1.0f);
So, to continue this lesson, why don't you try loading
the other heightmaps supplied in the source code zip files? See what
kind of terrains come out. Or even better; create your own heightmap
image!
Creating Quads from a Heightmap
With the above method we can create a Quad with varying height
but as I said earlier, we need a lot of quads to make realistic terrain
so now the problem is how to convert the heightmap into quads. This is
really not a problem. Just look at this image:
Here we created the
arrays necessary for describing a Mesh in the M3G system. The
VertexBuffer that holds two vertex arrays, the color array and the
position array. I have intentionally left out the allocation of the
color array from the above code, since I'll talk more about that later.
Right now let's focus on creating the quad.
Looks familiar doesn't it? A simple quad consisting of four vertrices that each have varying y-components but static x and z.
--------------------------------------------------------
源码见附件:
- Redikod_3D_tutorial_part_5_source_code.zip (25.3 KB)
- 下载次数: 2
发表评论
-
对Java的I/O流理解
2011-02-19 23:04 1959这是很久前另一个BLOG上的,现在不用了。转过来吧,方便查看. ... -
A*寻路(J2ME实现)
2011-02-19 23:00 1278这是很久前另一个BLOG上的,现在不用了。转过来吧,方便查看. ... -
J2ME上检测是否支持特定的API
2011-02-19 22:59 1513这是很久前另一个BLOG上的,现在不用了。转过来吧,方便查看. ... -
J2me paint[转]
2011-02-19 22:58 1427这是很久前另一个BLOG上的,现在不用了。转过来吧,方便查看. ... -
[JSR-184][3D编程指南(译文)]第一部分:快速进入移动JAVA 3D编程世界
2011-01-23 00:37 1705[英文原文&源码下载] ... -
[JSR-184][3D编程指南]Part IV:M3G built-in collision,light physics and camera perspec
2011-01-22 23:04 2121<!-- 整理收集自网络,收藏以便日后查阅 --> ... -
[JSR-184][3D编程指南]Part III: Particle systems and immediate mode rendering (2)
2011-01-22 22:56 1531<!-- 整理收集自网络,收藏以便日后查阅 --> ... -
[JSR-184][3D编程指南]Part III: Particle systems and immediate mode rendering (1)
2011-01-22 22:48 2218<!-- 整理收集自网络,收藏以便日后查阅 --> ... -
[JSR-184][3D编程指南]Part II: Light 3D theory and orientation
2011-01-22 22:29 1512<!-- 整理收集自网络,收藏以便日后查阅 --> ... -
[JSR-184][3D编程指南]Part I: Quick jump into the world of Mobile Java 3D programming
2011-01-22 22:07 2313<!-- 整理收集自网络,收藏以便日后查阅 --> ... -
[JSR-184][3D编程指南]目录索引
2011-01-22 21:25 1413Series of 3D programming tutori ... -
[Kuix][转]Kuix的事件处理机制
2009-10-08 18:19 1651原文连接 kuix这 ... -
[积累]getResourceAsStream()返回null的问题
2009-03-13 22:04 2645getResourceAsStream()可以获取JAR包内的 ... -
[资料]根据J2ME(MIDP)虚拟机对程序编写的优化方式
2009-02-27 09:39 14381、关于虚拟机 我认为 ... -
[资料]MIDP2.0中如何通过代码画半透明的圆和椭圆
2009-02-27 09:10 1600最近在做一个小Demo时,需要画一个半透明的圆,看遍M ... -
[资料]MIDP设计模式之集结贴[JavaME]
2009-02-23 22:07 13831: 架构性宣言: MI ... -
[资料]MVC在J2ME项目中的应用之MVC慨述
2009-02-23 21:48 1267内容提要: 本文简要的介绍了MVC模式的思想,并分析了M ... -
[资料]基于MVC模式的J2ME应用程序框架设计
2009-02-23 21:24 2848原文:http://www.mcu123.com/ ... -
[资料]线程在J2ME应用中的使用
2009-02-22 17:05 1593简要说明: 非常好的一篇文章,谈论到了线程各个方面的问题 ... -
[JSR-135][资料]渐进式下载
2009-02-22 16:17 1891Progressive download ...
相关推荐
【JSR-184】3D编程指南第二部分:光照3D理论与定向 在3D编程领域,理解和应用光照理论以及物体的定向是至关重要的。JSR-184,全称Java 3D API,是Java平台上的一个标准,它提供了用于创建和操作3D图形的强大工具。...
总之,【JSR-184】【3D编程指南】Part IV探讨了移动3D图形中的关键元素,包括碰撞检测、光照物理和相机视角控制,这些是创建生动、交互性3D应用的基础。通过学习和实践,开发者可以利用JSR-184在移动设备上创建...
【JSR-184】是Java Micro Edition (Java ME) 中的一个标准,全称为"Mobile 3D Graphics API",旨在为移动设备提供3D图形编程接口。这个标准允许开发者在小型设备上创建复杂的3D图形应用,比如游戏或者可视化工具。本...
【JSR-184】是Java Micro Edition (Java ME)平台中的一项规范,全称为“Mobile 3D Graphics API”。这个规范旨在为移动设备提供3D图形编程接口,使得开发者能够在小型设备上构建丰富的三维应用程序,如游戏、虚拟...
java.lang.ClassNotFoundException: javax.measure.converter.ConversionException所需的jar
综上所述,这个压缩包提供了JSR 184 3D图形技术的一个实例,通过DEMO程序,开发者可以学习如何在Java ME环境下利用JSR 184-m3g.jar库来创建和展示3D图形。M3G文件格式则使得3D模型能够在内存有限的移动设备上高效...
赠送jar包:undertow-websockets-jsr-2.1.7.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.1.7.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.1.7.Final-sources.jar; 赠送Maven依赖信息...
赠送jar包:undertow-websockets-jsr-2.1.7.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.1.7.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.1.7.Final-sources.jar; 赠送Maven依赖信息...
**JSR-135编程指导** JSR-135,全称为JavaTM Media Framework API,是Java ME(J2ME)平台中用于多媒体应用开发的重要规范。它为移动和嵌入式设备提供了处理音频、视频和图像的能力,使得开发者能够创建功能丰富的...
赠送jar包:jsr311-api-1.1.1.jar; 赠送原API文档:jsr311-api-1.1.1-javadoc.jar; 赠送源代码:jsr311-api-1.1.1-sources.jar; 赠送Maven依赖信息文件:jsr311-api-1.1.1.pom; 包含翻译后的API文档:jsr311-api...
赠送jar包:undertow-websockets-jsr-2.2.14.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.2.14.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.2.14.Final-sources.jar; 赠送Maven依赖信息...
赠送jar包:undertow-websockets-jsr-2.2.14.Final.jar; 赠送原API文档:undertow-websockets-jsr-2.2.14.Final-javadoc.jar; 赠送源代码:undertow-websockets-jsr-2.2.14.Final-sources.jar; 赠送Maven依赖信息...
用jsr184编写的手机3d编程实例,用户可以任意旋转箭头,放大缩小等等。包含如何使用数据定义mesh,如何操作camera如何旋转等等,程序功能较繁杂,但是界面较粗糙(数据定义的模型当然是越简单越好啦),学习意义大于...
《3-D Game Development on JSR-184 v1_0_3》是关于使用Java 3D技术在J2ME平台上开发3D游戏的一份重要资料,它为初学者提供了一个宝贵的入门教程。JSR-184,全称为Java ME 3D API,是Java Micro Edition(J2ME)平台...
赠送jar包:jackson-datatype-jsr310-2.12.5.jar; 赠送原API文档:jackson-datatype-jsr310-2.12.5-javadoc.jar; 赠送源代码:jackson-datatype-jsr310-2.12.5-sources.jar; 赠送Maven依赖信息文件:jackson-...
赠送jar包:jackson-datatype-jsr310-2.11.4.jar; 赠送原API文档:jackson-datatype-jsr310-2.11.4-javadoc.jar; 赠送源代码:jackson-datatype-jsr310-2.11.4-sources.jar; 赠送Maven依赖信息文件:jackson-...
标题:WebBeans -- JSR-299 描述:WebBeans是Gavin King的力作,专注于为Java EE平台提供上下文与依赖注入(Contexts and Dependency Injection)。 ### WebBeans (JSR-299) 知识点详解 #### 一、架构与合同 Web...
在实际开发中,我们通常会将这两个库添加到项目的类路径中,然后通过配置和编程API来启用和使用JSR-303验证。例如,在Spring框架中,可以通过在配置文件中启用注解驱动的验证,或者在代码中创建Validator实例并调用...