大家好,N3D-cpp版本的最新版1.5.0已经发布啦,在这里我向大家介绍一下引擎的一些基本用法。
您可以在这里下载windows平台的编译好的例子,在这里下载osx平台的,这里有生成的文档,这里是例子的源文件。

首先,我们来编译N3D,假设您是Windows平台开发,我们使用Msys+Mingw进行编译。

编译所需的第三方库的支持:

Opengl 版本 >= 3.2
Nest3d 版本 >= 1.5.0
ASSIMP 版本 >= 3.0
GLEW 版本 >= 1.10.0
GLFW 版本 >= 3.0.4
STB_image

在您下载好的N3D源文件中,我们选择msys.mf作为我们的Makefile,打开文件:

# msys + mingw

NEST_DIR = 
ASSIMP_DIR = 
GLEW_DIR = 
DEST_DIR = 

.PHONY: all
all:

将对应的正确的路径填入,之后make, make install, make clean即可生成N3D静态/动态库至DEST_DIR。

然后我们转到您下载好的例子的源文件里,选择msys.mf作为我们的Makefile,与刚才一样,打开并填入第三方库文件的正确位置,make, make install, make clean即可陆续生成目标可执行文件于bin/mingw/文件夹中。

这次我们讲解例子staticmesh的编写:

#include <iostream>
#include <exception>

#include "../utils.h"
#include "Nest3d.h"

using namespace nest;
using namespace std;

#ifdef __APPLE__
string assets_dir;
#elif defined _WIN32
string assets_dir("assets/staticmesh/");
#endif

首先我们添加utils.h的引用,utils.h包含了一些方便的功能,例如为我们载入图像文件,上传图像文件至GL,输出log等。
然后,assets_dir这个数组存放了当前例子的资源文件的具体地址,在win平台,staticmesh的资源文件存放于,bin/mingw/assets/staticmesh/中。

GLFWwindow *window = NULL;
// loop info
int TICKS_PER_SECOND = 25;
int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
int MAX_FRAMESKIP = 5;
// camera info
float speed = 10;
float sensitive = 0.01f;

注释loop info下的TICKS_PER_SECOND控制了游戏逻辑运行的帧频。
注释camera info则分别控制了摄像机移动的速度(ASWD)以及鼠标控制摄像机方向的灵敏度参数。

//////////////////////////////
// 初始化glfw,glew
//////////////////////////////
if(!initGLFWGLEW(800, 600, false)) exit(EXIT_FAILURE);
//////////////////////////////
// 初始化场景
//////////////////////////////
// 我们需要一个容器来储存场景中的Mesh
ContainerNode *root = new ContainerNode();
// 新建Mesh渲染模块,用来渲染Mesh到任意FBO
MeshRender *render = new MeshRender();
// 新建Container容器的Processor,用来对场景进行剔除操作,之后可以直接使用render绘制,或者输出剔除结果于数组中
ContainerProcessor *processor0 = new ContainerProcessor(root, render);
// 新建摄像机,存储投影矩阵,剔除视锥等模块
CameraNode *camera = new CameraNode(new FrustumCulling(0.7854f, 4 / 3, 1.0f, 1000.0f));
Matrix4::perspectiveFov(camera->projectionMatrix, 0.7854f, 4 / 3, 1.0f, 1000.0f);
camera->localMatrix.translate(Vector4(0.0f, 0.0f, 200.0f, 1.0f));
// 更新摄像机的矩阵树,详见ObjectNode.recompose(dt)
camera->recompose();
// 新建RenderTarget用来存放输出模块,FBO的信息就存放于此类中
RenderTarget *target = new RenderTarget("output_color", 0, 0, 800, 600);

在设置完成后,我们开始建立我们的虚拟场景:

// 我们使用MeshParser对模型文件进行解析
MeshParser *parser = new MeshParser();
parser->parse(
	assets_dir + "head.3ds", 
	aiProcess_GenNormals			|
	aiProcess_ValidateDataStructure	|
	aiProcess_Triangulate			|
	aiProcess_SortByPType
);
// 得到我们需要的head模型数据
Mesh *mesh0 = parser->meshes[0];
delete parser;

// 将模型的geometry信息传入opengl以得到渲染所需的VBOs(Vertex buffer object)
// 这里我们上传顶点位置,UV,法线数据
mesh0->geometry->createVBOs(GEOM_VERTEX | GEOM_UV | GEOM_NORMAL);

// 加载着色器
Shader *shader = new Shader();
// 载入glsl着色器文件,并用glBindAttribLocation()来指定顶点位置,UV,法线数据在glsl里的位置及名称
Shader::configure(
	shader, 
	GEOM_VERTEX | GEOM_UV | GEOM_NORMAL, 
	load_textfile((assets_dir + "head_vs.glsl").c_str()).c_str(), 
	load_textfile((assets_dir + "head_fs.glsl").c_str()).c_str()
);
// 生成VAO对象
shader->createVAO();
// 然后把我们模型的geometry的VBOs信息绑定到着色器的VAO对象
mesh0->geometry->bindVBOtoVAO(shader->vao, GEOM_VERTEX | GEOM_UV | GEOM_NORMAL);
// 在这里我们链接head_diffuse图像文件于着色器,使用名称texture_diffuse来访问,texture_specular同理。
shader->linkTexture(createTexture((assets_dir + "head_diffuse.jpg").c_str()), "texture_diffuse", true);
shader->linkTexture(createTexture((assets_dir + "head_specular.jpg").c_str()), "texture_specular", true);
// ShaderPart类用于链接glUniform数据,例如GLuint,GLfloat等数据。
// 在这里我们将灯光的位置作为3个GLfloat传入着色器。
shader->parts.push_back(new ShaderPart3f(glGetUniformLocation(shader->program, "light_pos"), 0.0f, 0.0f, 20.0f));
// 然后我们将此着色器存入mesh的ShaderMap(RenderTarget->flag,着色器)
// 这里使用map对象的原因是,我们可以根据不同的mesh对象,存储不同的着色器,因而在不同的RenderTarget上绘制相同的mesh时,可以有不同的输出
mesh0->shaderMap.insert(map<string, Shader*>::value_type(target->flag, shader));

// 最后我们将mesh传入一个MeshNode对象(MeshNode对象在被删除时,不会删除mesh对象,所以mesh对象的删除由我们负责,这也意味着我们可以让一个mesh,赋值给许多个MeshNode对象)
MeshNode *node0 = new MeshNode(mesh0);
// 我们设置MeshNode的本地矩阵进行缩放变换
node0->localMatrix.scale(Vector4(10.0f, 10.0f, 10.0f, 1.0f));
// 然后我们使用node0->recompose(0)来更新MeshNode的矩阵树
// 这里没有使用,是因为addChild函数会自动调用所添加对象的recompose(0)函数
root->addChild(node0);

之后我们就可以渲染场景啦:

while(!glfwWindowShouldClose(window))
{
	loops = 0;
	// 在这个loop中,我们更新场景的变换信息,使用固定的FPS
	// 得到一个旧的变换,和一个新的变换,最后再渲染中使用dt进行插值
	while(GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) 
	{
		// update game logic in TICKS_PER_SECOND
		update_camera(window);
		oldDir = newDir;
		newDir = camDir;
		oldPos = camera->worldMatrix * initPos;
		newPos = camera->worldMatrix * camPos;
		// 按ESC推出程序
		if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		{
			escFlag = true;
			break;
		}
		next_game_tick += SKIP_TICKS;
		loops++;
	}
	if(escFlag) break;
	// 在最大的fps下渲染场景
	// 首先计算dt(0-1)
	dt = float(GetTickCount() + SKIP_TICKS - next_game_tick) / float(SKIP_TICKS);
	// 使用dt更新摄像机变换
	camera->localMatrix.translate(oldPos + (newPos - oldPos) * dt);
	camera->localMatrix.rotate(oldDir + (newDir - oldDir) * dt);
	camera->recompose();
	// 将摄像机的位置更新至我们的着色器中,以模拟光线是从摄像机的位置照射而来
	ShaderPart3f *light_pos = static_cast<ShaderPart3f*>(shader->parts[0]);
	light_pos->v0 = camera->worldMatrix.raw[12];
	light_pos->v1 = camera->worldMatrix.raw[13];
	light_pos->v2 = camera->worldMatrix.raw[14];
	render->numDraws = 0;
	render->numTris = 0;
	render->numVts = 0;
	// 告诉render我们目前的渲染目标
	render->flag = target->flag;
	// 绑定渲染目标的FBO
	glBindFramebuffer(GL_FRAMEBUFFER, target->frameBuffer);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);
	glViewport(target->x, target->y, target->width, target->height);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	// 进行剔除操作,并将结果直接渲染于FBO
	processor0->calculate(camera, true, NULL, NULL, NULL);
	//cout<<render->numDraws<<endl;
	// 交换BUFFER以显示渲染结果至窗口
	glfwSwapBuffers(window);
	glfwPollEvents();
}

下面是运行结果的截图:

staticmesh

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>