65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 和 OpenGL 实现 3D 广场探索

starIconstarIconstarIconstarIconstarIcon

5.00/5 (20投票s)

2012 年 1 月 10 日

CPOL

3分钟阅读

viewsIcon

64222

downloadIcon

7468

在 OpenGL 和 C# 中探索一个简单的 3D 广场

cover

引言

这个演示使用了TAO框架,它基本上是一个从.NET到Windows OpenGL的Interop库。它还包含一个由我自己编写的小引擎,名为ShadowEngine ,它封装了图形初始化、纹理和模型加载等任务。

好的,让我一步一步地解释这个项目。

SkyBox.cs

这个类用于绘制你在演示中看到的天空和山脉,它基本上是一个带有图像的盒子。这种技术被用于当今90%的游戏中,当然,它会与其他技术混合使用以使其更逼真。

看看我是如何绘制天空盒的一部分的

Gl.glEnable(Gl.GL_TEXTURE_2D);    // enable opengl texturing  
Gl.glBindTexture(Gl.GL_TEXTURE_2D,ContentManager.GetTextureByName
    ("back.bmp"));                //enabling texture for the portion of the skybox

Gl.glBegin(Gl.GL_QUADS);
Gl.glNormal3d(-1, 1, 1);
Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex3d(x + width, y, z);
Gl.glNormal3d(-1, -1, 1);
Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex3d(x + width, y + height, z);
Gl.glNormal3d(1, -1, 1);
Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex3d(x, y + height, z);
Gl.glNormal3d(1, 1, 1);
Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex3d(x, y, z);
Gl.glEnd();

使用Gl.glVertex3d 我在屏幕上绘制一个正方形的点,使用Gl.glTexCoord2f, 我告诉OpenGL正方形上的图像在哪里,使用Gl.glNormal3d, 我告诉openGL正方形的方向。所有这些调用都是在Gl.glBegin(Gl.GL_QUADS)Gl.glEnd()之间进行的。

Plaza.cs 和 碰撞

这个类处理绘制广场,它基本上是用纹理绘制3D模型。这个过程被封装起来,所以你只能在create 函数中看到它

m = ContentManager.GetModelByName("plaza.3DS"); // get the model
m.CreateDisplayList();    // this loads the model in opengl memory(displays lists)

以及在Draw 函数中

Gl.glPushMatrix();
Gl.glTranslatef(0, 1.4f, 0); 
Gl.glScalef(0.2f, 0.2f, 0.2f); 
m.DrawWithTextures();
Gl.glPopMatrix();

广场的碰撞以一种非常简单的方式实现,并且仅用于学习目的,因为当今的游戏使用非常复杂的架构来处理碰撞,这让地球上的普通人无法理解。碰撞针对摄像机,并且有两种类型的碰撞。第一种是碰撞点,例如喷泉和支撑旗帜的杆子,对于这种碰撞,你只需要告诉碰撞管理器这些点的3D位置,然后告诉一个数字,即在触发碰撞之前你希望摄像机靠近它的距离。另一种是碰撞线,例如,如果你不希望用户离开广场,则会有四条碰撞线,这里是代码的示例。

public void CreateCollisions()
{
    CollisionPoint c1 = new CollisionPoint();
    c1.point = new Point3D(1.5f, 9.0f, 0);
    c1.ColitionDistance = 2.3f;
    c1.enabled = true; 

    CollisionPoint c2 = new CollisionPoint();
    c2.point = new Point3D(-0.35f, -3.97f, 0);
    c2.ColitionDistance = 0.3f;
    c2.enabled = true;

    Collision.AddCollisionPoint(c1);
    Collision.AddCollisionPoint(c2); 
 
    Collision.AddCollisionSegment(new Point3D(-14.4f, 28.3f, 0), 
                new Point3D(-14.5f, -11.6f, 0), 0.5f);
    Collision.AddCollisionSegment(new Point3D(-14.4f, -11.6f, 0), 
                new Point3D(20.1f, -11.7f, 0), 0.5f);
}  

Flag.cs

解释旗帜的制作超出了本文的范围,我从www.nehe.com的旗帜教程中借用了大部分代码。为了使一个困难的解释变得容易,我告诉说旗帜是一个点的网格,这些点乘以一个正弦函数,为什么是正弦函数,看看正弦函数是什么样的,你就会明白。

3DPlazaInCSharp/image002.png

然后,每一帧我都会绘制,我将所有点从左向右移动一步,并将最右边的点移动到第一个左边的点。瞧,你看到一面飘扬的旗帜。这是代码的一部分

for (int x = 0; x < 47; x++)
     {
     // Loop Through The Y Plane
     for (int y = 0; y < 10; y++)
         {
             // Apply The Wave To Our Mesh
             points[x, y, 0] = (float)((x / 5.0f) - 0.1f);
             points[x, y, 1] = (float)((y / 1.125f) - 0.1f);
             points[x, y, 2] = 
(float)(Math.Sin(Helper.DegreeToRad(((x/ 5.0f) * 40.0f)) * 2.0f));
          }      
     }

Camera.cs

这个类用于处理摄像机的移动;它有一个第一人称射击摄像机。这个想法是将鼠标放在屏幕的中心,并将鼠标在X和Y方向上的每次移动转换为角度旋转,然后再次将鼠标放在屏幕中心。此外,摄像机在向某个方向移动之前会查询碰撞管理器。这是一个例子

if (!Collision.CheckCollision(new Point3D(-newEyeX, -newEyeZ, 0)))
   {
      eyex = newEyeX;
      eyez = newEyeZ;
   }

MainClass.cs 和 Mainform.cs

Mainclass Manager 类,它包含将在屏幕上绘制的所有对象,如果你希望某个对象不被绘制,只需在draw 函数中注释掉该对象即可

public void DrawScene()
        {
            plaza.Draw();
            sky.Draw();
            flag.Draw();  
            //DebugMode.WriteCamaraPos(200, 200); //work only in
                                                 32bits system
            Collision.DrawColissions();         
        }

Mainform 是显示项目的Windows Form。它包含一个用于绘制和更新整个场景的计时器。

我希望收到关于这个例子的反馈。如果你喜欢它,你可以在下面留言。

历史

  • 2014年7月15日:初始版本
© . All rights reserved.