当前位置: 代码迷 >> GIS >> 渲染地形解决思路
  详细解决方案

渲染地形解决思路

热度:324   发布时间:2016-05-05 06:38:03.0
渲染地形
很多3D游戏都需要地形,建立并渲染地形,再加上物理效果,比如驾驶一辆小汽车在场景行使,你会觉得这很困难。而这篇文章将讲解这种技术。
为了编译其中的程序代码,你需要:
一个C#编译器,最好是Visual Studio .NET
DIRECTX9.0c开发工具
一块显卡并且支持shader2.0以上,因为除非你不想它运行的很慢。(比如只有10FPS)
我只是推荐你,最好精通C#,和Managed DirectX。
开始,
首先你在渲染地形时,通常是用到一张带灰度信息的位图。这通常是一个容易的手段。这篇文章介绍的是使用一种灰度高度比例贴图。这就是我用到的贴图,你也可以自己去修改它。


我们将用两个纹理,一个草地,一个土地。地形比较高的部分用草地文理。我们将两种纹理混合在一起,这就是根据位图的灰度做到的。
这里用到一种语言High Level Shader Language
熟悉它你就会知道这是使用更高级的顶点和纹理的渲染方式。由于HLSL和C很相似,所以懂C的可以不费劲掌握它。以下的就是将两个纹理混合的HLSL程序:

float4x4 WorldViewProj;float4 light;
void Transform(
  in float4 inPos : POSITION0,
  in float2 inCoord : TEXCOORD0, 
  in float4 blend : TEXCOORD1,
  in float3 normal : NORMAL, 
  out float4 outPos : POSITION0,
  out float2 outCoord : TEXCOORD0,
  out float4 Blend : TEXCOORD1,
  out float3 Normal : TEXCOORD2,
  out float3 lightDir : TEXCOORD3 )
{
  outPos = mul(inPos, WorldViewProj);
  outCoord = inCoord;
  Blend = blend;
  Normal = normalize(mul(normal,WorldViewProj));
  lightDir = inPos.xyz - light; 
}

看上去HLSL和C的语法结构相似:再调节名称时,input,output变量前经常会有语意的定义。input通常是从程序中接受变量然后传导output。你也许注意到TEXCOORD被使用了很多次。这是因为TEXCOORD在程序中使用了多次,一个变量不提供位置,法线等。而HLSL包含了计算函数如mul(),normalize().在MSDN里可以知道使用方法。想知道更多关于HLSL方面的知识。推荐去访问相关的一些网站。

现在解释一下HLSL代码含义:首先input接受position,position在这里是和投影矩阵相乘之后得到的。顶点将被移动到相机的空间。input接受纹理坐标并混合两种纹理。法线也被移动。最后平行光被减去光源的坐标(该例,世界空间是和对象空间一致的所以不会有改变)将其传给像素渲染,以下是像素渲染代码:
Texture Texture1;
Texture Texture2;

sampler samp1 = sampler_state { texture = <Texture1>; 
minfilter = LINEAR; mipfilter = LINEAR; magfilter = LINEAR;};
sampler samp2 = sampler_state { texture = <Texture2>; 
minfilter = LINEAR; mipfilter = LINEAR; magfilter = LINEAR;};
float4 TextureColor(
  in float2 texCoord : TEXCOORD0,
  in float4 blend : TEXCOORD1,
  in float3 normal : TEXCOORD2,
  in float3 lightDir : TEXCOORD3) : COLOR0
{
  float4 texCol1 = tex2D(samp1, texCoord*4) * blend[0];
  float4 texCol2 = tex2D(samp2, texCoord) * blend[1];
  return (texCol1 + texCol2) * (saturate(dot(normalize(normal), 
  normalize(light)))* (1-ambient) + ambient);
}

正象你所看到的,像素渲染器使用了所有顶点渲染器中的变量,除了POSITION0,因为POSITION0是所有输出所用到的。首先两个纹理将用tex2D()做计算,tex2D()不影响文理只是一个处理器而已。这些颜色根据混合值相乘并加上光线的密度,然后反回结果。返回值是float4由COLOR0语法做了标记。每个像素渲染器都要返回一个这样的值。

再回到C#
为了使程序和SHADER通信,必须有顶点声明。将通知DIRECTX数据在顶点缓冲以及相关的input顶点渲染变量,以下是顶点声明代码:
VertexElement[] v = new VertexElement[] 

  new VertexElement(0,0,DeclarationType.Float3,DeclarationMethod.Default,
  DeclarationUsage.Position,0),
  new VertexElement(0,12,DeclarationType.Float3,DeclarationMethod.Default,
  DeclarationUsage.Normal,0),
  new VertexElement(0,24,DeclarationType.Float2,DeclarationMethod.Default,
  DeclarationUsage.TextureCoordinate,0),
  new VertexElement(0,32,DeclarationType.Float4,DeclarationMethod.Default,
  DeclarationUsage.TextureCoordinate,1), 
  VertexElement.VertexDeclarationEnd
}; 

decl = new VertexDeclaration(device,v);

正如你看到的,顶点声明包含一个数组VertexElements描述了顶点的结构。说道结构,你不能自己定义一个CustomVertex,因为每一个文理参数必须精确到每一顶点。所以结构必须是以下:
public struct Vertex
{
  Vector3 pos;
  Vector3 nor; 
  float tu,tv;
  float b1,b2,b3,b4; 
  public Vertex(Vector3 p,Vector3 n,
  float u,float v,float B1,float B2,
  float B3, float B4, bool normalize)
  {
  pos = p;nor = n;tu = u;tv = v;
  b1=B1; b2=B2; b3=B3;b4 = B4;
  相关解决方案