OSG编程指南<十六>:OSG渲染到纹理RTT及三维纹理体渲染技术简介

news2024/9/22 3:37:08

1、渲染到纹理(RTT)

1.1 RTT介绍

  RTT(Render to Texture)即渲染到纹理。在普通的图形渲染流程中,最终结果是渲染到帧缓存中,然后才会显示到屏幕上。而RTT则是将场景渲染到一张纹理上,并且在之后进行使用。

1)创建纹理对象

  要进行RTT,首先要创建一个纹理对象,需要注意的是,这里只是创建了一个纹理对象,并没有提供数据,因此data设置成了null。

// 创建渲染对象
const targetTextureWidth = 256;   // 纹理的长度与宽度
const targetTextureHeight = 256;
const targetTexture = gl.createTexture();  // 创建纹理对象
gl.bindTexture(gl.TEXTURE_2D, targetTexture); // 进行bind,以下操作针对的是当前bind对象
 
{
  // 定义 0 级的大小和格式,不需要mipmap只需定义level 0即可
  const level = 0;
  const internalFormat = gl.RGBA; // 每个像素使用RGBA表示
  const border = 0;                         // 纹理的border,必须为0
  const format = gl.RGBA;
  const type = gl.UNSIGNED_BYTE; // 像素每个通道的存储格式
  const data = null;                             // 设置成null
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                targetTextureWidth, targetTextureHeight, border,
                format, type, data);
 
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
}

2)创建帧缓冲

  接下来,需要创建一个帧缓冲(framebuffer),帧缓冲是一个附件集,附件是纹理或renderbuffer。Renderbuffer与纹理很像,但支持一些纹理不支持的格式和可选项。不过,不能像纹理那样直接将renderbuffer提供给着色器。

// 创建并绑定帧缓冲
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
 
// 附加纹理为第一个颜色附件
const attachmentPoint = gl.COLOR_ATTACHMENT0;
gl.framebufferTexture2D(
    gl.FRAMEBUFFER, attachmentPoint, gl.TEXTURE_2D, targetTexture, level);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

3) 创建深度附件

  在渲染到纹理时,可以选择不创建深度附件,这样就没有深度检测,渲染出来的纹理遮挡关系可能不正确。如果需要创建深度附件,则可以按照以下步骤进行:

// 创建一个深度纹理
const depthTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depthTexture);
 
// 设置深度缓冲的大小和targetTexture相同
{
  // 定义 0 级的大小和格式
  const level = 0;
  const internalFormat = gl.DEPTH_COMPONENT24;
  const border = 0;
  const format = gl.DEPTH_COMPONENT;
  const type = gl.UNSIGNED_INT;
  const data = null;
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
                targetTextureWidth, targetTextureHeight, border,
                format, type, data);
 
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
 
  // 将深度纹理附加到缓冲帧
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTexture, level);
}

4)进行渲染,生成纹理

  在渲染之前需要先绑定帧缓冲以及设置视口。以下是进行渲染的代码:

// 绑定帧缓冲
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);

// 设置视口大小
gl.viewport(0, 0, targetTextureWidth, targetTextureHeight);

// 清空画布颜色缓冲区和深度缓冲区
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

// 设置相机
// ...

// 设置光源
// ...

// 绘制场景

// …

1.2 RTT应用场景

  渲染到纹理,目前我知道的使用场景有以下几种:

1)直接交给着色器使用

  生成的纹理可以直接传递给着色器,在着色器中使用它进行渲染。在进行渲染之前,需要将Framebuffer的绑定解除,并使用纹理对象进行绘制:

// 将帧缓冲的绑定解除
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 
// 使用生成的纹理进行绘制
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 6);

2)读取纹理到数组,然后进一步使用, 可用于拾取对象、生成纹理、调试Shader等

// 读取纹理到数组
const pixels = new Uint8Array(targetTextureWidth * targetTextureHeight * 4);
gl.readPixels(0, 0, targetTextureWidth, targetTextureHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

  在这里,我们使用gl.readPixels()函数将帧缓冲区中的像素数据读取到一个数组中。这个数组就是我们最终生成的纹理数据,可以用来在之后的渲染中使用。如果想用这个数组重新生成纹理,可按以下代码:

// 创建纹理对象
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

// 设置纹理参数
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// 将像素数据绑定到纹理对象上
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, targetTextureWidth, targetTextureHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

  有些开发者将Shader的中间值渲染到纹理用于调试。有些软件将场景物体的ID渲染到纹理,用于物体的拾取。

3)Shadowmap

  在 Shadow Mapping 中,首先需要渲染一次场景,生成深度贴图(Depth Map),这个深度贴图实际上就是一张纹理,存储了从光源视角看到的场景的深度信息。然后,再将深度贴图应用到场景中进行阴影计算。渲染到纹理是实现 Shadow Mapping 的一种基础技术。

1.3 OSG中的RTT

  在 OSG 中主要有以下 4 种 buffer:
FrameBuffer。相当于一个本地缓存集合,包括颜色缓存、深度缓存、模板缓存和累积缓存,用于存放每帧的渲染数据。
Frame Buffer Object。是 OpenGL 的一个高级扩展,提供了一种渲染到目标对象的新的机制。
Pixel Buffer Objects。是窗口系统的一个扩展,实现离屏渲染,与一个不可见的窗口图形环境相似。
Vertex Buffer Object。是 OpenGL 的一个高级扩展,用于将顶点数据提交给显卡的高速缓存区,使显卡能够快速存取,以提高渲染速度。

  渲染到纹理就是将当前的渲染结果(framebuffer)通过纹理的方式直接读取,这样可以在很大程度上提高渲染的性能,避免从 framebuffer 里面拷贝纹理对象,从而节省很大的内存。渲染到纹理主要用于生成动态纹理、反射效果和图像模糊等。下面通过示例程序向读者演示如何渲染到纹理。

1.4 OSG中的RTT示例

在这里插入图片描述

1.5 源码

#include <windows.h>
#include <osgViewer/Viewer>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Quat>
#include <osg/Matrix>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Texture2D>
#include <osg/Stencil>
#include <osg/ColorMask>
#include <osg/GLExtensions>
#include <osg/Depth>
#include <osg/AnimationPath>
#include <osg/Transform>
#include <osg/Material>
#include <osg/NodeCallback>
#include <osg/Depth>
#include <osg/CullFace>
#include <osg/TexMat>
#include <osg/TexGen>
#include <osg/TexEnv>
#include <osg/TextureCubeMap>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osgDB/WriteFile>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <iostream>

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")

//定义相机绘制回调
struct MyCameraPostDrawCallback : public osg::Camera::DrawCallback
{
public:

	MyCameraPostDrawCallback(osg::ref_ptr<osg::Image> image) :
		_image(image)
	{

	}

	virtual void operator () (const osg::Camera& /*camera*/) const
	{
		if (_image && _image->getPixelFormat() == GL_RGBA && _image->getDataType() == GL_UNSIGNED_BYTE)
		{
			//获得Image的中心
			int column_start = _image->s() / 4;
			int column_end = 3 * column_start;

			int row_start = _image->t() / 4;
			int row_end = 3 * row_start;

			//将像素数据进行反向
			for (int r = row_start; r < row_end; ++r)
			{
				unsigned char* data = _image->data(column_start, r);
				for (int c = column_start; c < column_end; ++c)
				{
					(*data) = 255 - (*data); ++data;
					(*data) = 255 - (*data); ++data;
					(*data) = 255 - (*data); ++data;
					(*data) = 255; ++data;
				}
			}
			_image->dirty();
		}
		else if (_image && _image->getPixelFormat() == GL_RGBA && _image->getDataType() == GL_FLOAT)
		{
			//获得Image的中心
			int column_start = _image->s() / 4;
			int column_end = 3 * column_start;

			int row_start = _image->t() / 4;
			int row_end = 3 * row_start;

			//将像素数据进行反向
			for (int r = row_start; r < row_end; ++r)
			{
				float* data = (float*)_image->data(column_start, r);
				for (int c = column_start; c < column_end; ++c)
				{
					(*data) = 1.0f - (*data); ++data;
					(*data) = 1.0f - (*data); ++data;
					(*data) = 1.0f - (*data); ++data;
					(*data) = 1.0f; ++data;
				}
			}
			_image->dirty();
		}
	}

public:

	osg::ref_ptr<osg::Image> _image;
};

//创建预渲染场景
osg::ref_ptr<osg::Node> createPreRenderSubGraph(osg::ref_ptr<osg::Node> subgraph,
	unsigned tex_width,
	unsigned tex_height,
	osg::Camera::RenderTargetImplementation renderImplementation,
	bool useImage)
{
	if (!subgraph) return 0;

	//创建一个包含预渲camera的 Group 节点
	osg::ref_ptr<osg::Group> parent = new osg::Group;

	//创建纹理,用来绑定相机渲染的结果
	osg::ref_ptr<osg::Texture> texture = 0;
	{
		osg::ref_ptr<osg::Texture2D> texture2D = new osg::Texture2D;
		texture2D->setTextureSize(tex_width, tex_height);
		texture2D->setInternalFormat(GL_RGBA);
		texture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::LINEAR);
		texture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::LINEAR);

		texture = texture2D;
	}

	//创建一个用来浏览的四边形几何体
	{
		osg::ref_ptr<osg::Geometry> polyGeom = new osg::Geometry();
		//设置该几何体不使用显示列表
		polyGeom->setSupportsDisplayList(false);

		float height = 100.0f;
		float width = 200.0f;

		//创建顶点数组,并添加数据
		osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array;
		vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
		vertices->push_back(osg::Vec3(width, 0.0f, 0.0f));
		vertices->push_back(osg::Vec3(width, 0.0f, height));
		vertices->push_back(osg::Vec3(0.0f, 0.0f, height));

		//创建纹理数组,并添加数据
		osg::ref_ptr<osg::Vec2Array> texcoords = new osg::Vec2Array();
		texcoords->push_back(osg::Vec2(0.0f, 0.0f));
		texcoords->push_back(osg::Vec2(1.0f, 0.0f));
		texcoords->push_back(osg::Vec2(1.0f, 1.0f));
		texcoords->push_back(osg::Vec2(0.0f, 1.0f));

		polyGeom->setVertexArray(vertices.get());

		//使用vbo扩展
		{
			osg::ref_ptr<osg::VertexBufferObject> vbObject = new osg::VertexBufferObject;
			vertices->setVertexBufferObject(vbObject);

			polyGeom->setUseVertexBufferObjects(true);
		}

		polyGeom->setTexCoordArray(0, texcoords.get());

		//创建颜色数组,并设置绑定方式及添加数据
		osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array;
		colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
		polyGeom->setColorArray(colors.get());
		polyGeom->setColorBinding(osg::Geometry::BIND_OVERALL);

		polyGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertices->size()));

		//现在我们需要将纹理附加到该几何体上,我们创建一个包含该纹理的StateSet
		osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;

		stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);

		polyGeom->setStateSet(stateset);

		osg::ref_ptr<osg::Geode> geode = new osg::Geode();
		geode->addDrawable(polyGeom.get());

		parent->addChild(geode.get());

	}

	// 需要创建一个相机节点,用来渲染到该纹理(RTT)
	{
		osg::ref_ptr<osg::Camera> camera = new osg::Camera;

		//设置背景色及清除颜色和深度缓存
		camera->setClearColor(osg::Vec4(0.1f, 0.1f, 0.3f, 1.0f));
		camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		//获得该节点的范围盒
		const osg::BoundingSphere& bs = subgraph->getBound();
		if (!bs.valid())
		{
			return subgraph.get();
		}

		float znear = 1.0f * bs.radius();
		float zfar = 3.0f * bs.radius();

		float proj_top = 0.25f * znear;
		float proj_right = 0.5f * znear;

		znear *= 0.9f;
		zfar *= 1.1f;

		//设置投影矩阵.
		camera->setProjectionMatrixAsFrustum(-proj_right, proj_right, -proj_top, proj_top, znear, zfar);

		//将相机对准该子场景
		camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
		camera->setViewMatrixAsLookAt(bs.center() - osg::Vec3(0.0f, 2.0f, 0.0f) * bs.radius(), bs.center(), osg::Vec3(0.0f, 0.0f, 1.0f));

		//设置视口
		camera->setViewport(0, 0, tex_width, tex_height);

		//设置相机的渲染序列
		camera->setRenderOrder(osg::Camera::PRE_RENDER);

		//设置相机渲染通过 OpenGL frame buffer object 实现
		camera->setRenderTargetImplementation(renderImplementation);


		if (useImage)
		{
			osg::ref_ptr<osg::Image> image = new osg::Image;
			//image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
			image->allocateImage(tex_width, tex_height, 1, GL_RGBA, GL_FLOAT);

			//将Image附加到相机的COLOR_BUFFER
			camera->attach(osg::Camera::COLOR_BUFFER, image.get());

			//添加相机的绘制后回调,修改images数据
			camera->setPostDrawCallback(new MyCameraPostDrawCallback(image.get()));

			//这里我们不直接将相机的COLOR_BUFFER附加到该纹理上,是为了修改渲染后的图像数据
			texture->setImage(0, image.get());
		}
		else
		{
			//直接将该纹理附加到相机的颜色缓存.
			camera->attach(osg::Camera::COLOR_BUFFER, texture.get());
		}

		//添加要绘制的子场景
		camera->addChild(subgraph.get());

		parent->addChild(camera.get());
	}

	return parent.get();
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	unsigned tex_width = 1024;
	unsigned tex_height = 512;

	osg::Camera::RenderTargetImplementation renderImplementation = osg::Camera::FRAME_BUFFER_OBJECT;

	bool useImage = false;

	//读取模型
	osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("cessna.osg");

	//创建一个transform节点,用来选装该子场景
	osg::ref_ptr<osg::MatrixTransform> loadedModelTransform = new osg::MatrixTransform;
	loadedModelTransform->addChild(loadedModel.get());

	//设置更新回调
	osg::ref_ptr<osg::NodeCallback> nc = new osg::AnimationPathCallback(loadedModelTransform->getBound().center(), osg::Vec3(0.0f, 0.0f, 1.0f), osg::inDegrees(45.0f));
	loadedModelTransform->setUpdateCallback(nc);

	osg::ref_ptr<osg::Group> rootNode = new osg::Group();
	rootNode->addChild(createPreRenderSubGraph(loadedModelTransform.get(), tex_width, tex_height, renderImplementation, useImage));

	//优化场景数据
	osgUtil::Optimizer optimzer;
	optimzer.optimize(rootNode.get());

	//方便查看在多边形之间切换,以查看三角网
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());
	viewer->setSceneData(rootNode.get());
	viewer->setUpViewInWindow(600, 600, 1000, 800);

	return viewer->run();
}

2、三维纹理映射

2.1 三维纹理映射介绍

  三维纹理(3D texture),即体纹理(volume texture),是传统二维纹理(2D texture)在逻辑上的扩展。二维纹理是一张简单的位图图片,用于为三维模型提供表面点的颜色值;而一个三维纹理,可以被认为由很多张 2D 纹理组成,用于描述三维空间数据的图片。三维纹理通过三维纹理坐标进行访问 。

  优势:使用体纹理,可以跳过为三维网格确定良好二维参数的复杂过程,因为三维位置可以直接用作纹理坐标,从而避免了二维参数化中通常会发生的变形和接缝问题。体纹理也可用于表示诸如木材或大理石的材料的体积结构。使用三维纹理实现出的这些模型,看起来会很逼真,浑然天成。

  劣势:更高的储存要求,并且滤波成本更高。使用体纹理作为表面纹理会非常低效,因为三维纹理中的绝大多数样本都没起到作用。

  三维纹理映射(osg::Texture3D)是一大类应用范畴的一部分,称为体纹理。三维纹理主要应用于医学领域和科学领域,笔者也未对其做深入研究,只限于简单的了解,这里也只简单介绍一下。在OsgChina 中国官方成员中,hesicong 目前主要研究这个方向。在医学领域应用程序中,三维纹理主要应用于断层计算成像(CT)和核磁共振(MRT)图像,在网上有很多相关的演示视频。当然,这里也不会向读者演示一个精妙的虚拟手术,只是作一些简单的介绍。

  在实际的虚拟现实项目中,三维纹理的应用不多,它虽然能达到很好的效果,但当面对一个很大的场景时,渲染的负担是非常大的,三维纹理可能也会非常之大,它占用的内存资源也会非常多,有时即使是一个非常粗糙的三维纹理,它占用的内存也可能是普通二维纹理的 16 倍或 32 倍。如果渲染一个大的场景,就需要一个高配置的机器。

  osg::Texture3D 类继承自 osg::Texture 类,封装了 OpenGL 的二维纹理函数的一些功能,但它不支持立方图纹理。在它的父类中同样有 osg::StateAttribute。因此,它同样可以通过设置渲染属性来启用三维纹理属性。到目前为止,我们已经讲解了基本的几种纹理映射,从它们的类的继承关系可以看出,它们都继承自 osg::Texture。

2.2 示例

在这里插入图片描述

2.3 源码

#include <windows.h>
#include <osgViewer/Viewer>
#include <osg/Vec3>
#include <osg/Vec4>
#include <osg/Quat>
#include <osg/Matrix>
#include <osg/ShapeDrawable>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Notify>
#include <osg/MatrixTransform>
#include <osg/Texture3D>
#include <osg/Stencil>
#include <osg/ColorMask>
#include <osg/GLExtensions>
#include <osg/Depth>
#include <osg/AnimationPath>
#include <osg/Transform>
#include <osg/Material>
#include <osg/NodeCallback>
#include <osg/Depth>
#include <osg/CullFace>
#include <osg/TexMat>
#include <osg/TexGen>
#include <osg/TexEnv>
#include <osg/TextureCubeMap>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osgDB/WriteFile>
#include <osgDB/ReadFile>

#include <osgUtil/Optimizer>

#include <iostream>

#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")

//创建一个四边形节点
osg::ref_ptr<osg::Node> createNode()
{
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

	//设置顶点
	osg::ref_ptr<osg::Vec3Array> vc = new osg::Vec3Array();
	vc->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
	vc->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
	vc->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
	vc->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));

	geom->setVertexArray(vc.get());

	//设置纹理坐标
	osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
	vt->push_back(osg::Vec2(0.0f, 0.0f));
	vt->push_back(osg::Vec2(1.0f, 0.0f));
	vt->push_back(osg::Vec2(1.0f, 1.0f));
	vt->push_back(osg::Vec2(0.0f, 1.0f));

	geom->setTexCoordArray(0, vt.get());

	//设置法线
	osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
	nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));

	geom->setNormalArray(nc.get());
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

	//添加图元
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));

	//绘制
	geode->addDrawable(geom.get());

	return geode.get();
}

//初始化一个图形环境
class MyGraphicsContext
{
public:
	MyGraphicsContext()
	{
		//设置图形环境特性
		osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
		//设置左上角坐标
		traits->x = 0;
		traits->y = 0;
		//设置宽度和高度
		traits->width = 1;
		traits->height = 1;
		//设置窗口扩展
		traits->windowDecoration = false;
		//设置双缓冲
		traits->doubleBuffer = false;
		traits->sharedContext = 0;
		//设置pbuffer
		traits->pbuffer = true;

		//创建图形环境
		_gc = osg::GraphicsContext::createGraphicsContext(traits.get());

		//如果创建失败
		if (!_gc)
		{
			//设置pbuffer为false
			traits->pbuffer = false;
			//重新创建图形环境
			_gc = osg::GraphicsContext::createGraphicsContext(traits.get());
		}

		//是否初始化
		if (_gc.valid())
		{
			//初始化
			_gc->realize();
			_gc->makeCurrent();
		}
	}

	bool valid() const { return _gc.valid() && _gc->isRealized(); }

private:
	osg::ref_ptr<osg::GraphicsContext> _gc;
};

//创建三维纹理属性
osg::ref_ptr<osg::StateSet> createState()
{
	//创建图形环境
	MyGraphicsContext gc;
	if (!gc.valid())
	{
		//如果创建失败,则返回
		osg::notify(osg::NOTICE) << "Unable to create the graphics context required to build 3d image." << std::endl;
		return 0;
	}

	//读取四张二维纹理图像
	osg::ref_ptr<osg::Image> image_0 = osgDB::readImageFile("Images/lz.rgb");
	osg::ref_ptr<osg::Image> image_1 = osgDB::readImageFile("Images/reflect.rgb");
	osg::ref_ptr<osg::Image> image_2 = osgDB::readImageFile("Images/tank.rgb");
	osg::ref_ptr<osg::Image> image_3 = osgDB::readImageFile("Images/skymap.jpg");

	//判断是否正确读取
	if (!image_0 || !image_1 || !image_2 || !image_3)
	{
		std::cout << "Warning: could not open files." << std::endl;

		return new osg::StateSet();
	}

	//判断纹理格式是否一致
	if (image_0->getPixelFormat() != image_1->getPixelFormat() || image_0->getPixelFormat() != image_2->getPixelFormat() || image_0->getPixelFormat() != image_3->getPixelFormat())
	{
		std::cout << "Warning: image pixel formats not compatible." << std::endl;

		return new osg::StateSet();
	}

	//得到支持的最大的三维纹理单元的大小
	/*GLint textureSize = osg::Texture3D::getExtensions(0, true)->maxTexture3DSize();
	if (textureSize > 256)
	{
		textureSize = 256;
	}*/

	GLint textureSize = 256;

	//对四张二维纹理图像缩放,以达到相同的大小
	image_0->scaleImage(textureSize, textureSize, 1);
	image_1->scaleImage(textureSize, textureSize, 1);
	image_2->scaleImage(textureSize, textureSize, 1);
	image_3->scaleImage(textureSize, textureSize, 1);

	//创建一个三维纹理数据图像,注意深度为4
	osg::ref_ptr<osg::Image> image_3d = new osg::Image;
	//第一个和第二个参数是纹理的大小,第三个参数指的是三维纹理数据图像的深度
	image_3d->allocateImage(textureSize, textureSize, 4, image_0->getPixelFormat(), image_0->getDataType());

	//把四张二维纹理图像压入三维纹理数据图像
	//第1-3个参数分别是s,t,r上的偏移,当然这里只是r上的偏移
	//第四个参数是子二维纹理图像数据
	image_3d->copySubImage(0, 0, 0, image_0.get());
	image_3d->copySubImage(0, 0, 1, image_1.get());
	image_3d->copySubImage(0, 0, 2, image_2.get());
	image_3d->copySubImage(0, 0, 3, image_3.get());

	//设置纹理格式
	image_3d->setInternalTextureFormat(image_0->getInternalTextureFormat());

	//创建三维纹理对象
	osg::ref_ptr<osg::Texture3D> texture3D = new osg::Texture3D;
	//设置滤波,不支持mip map滤波
	texture3D->setFilter(osg::Texture3D::MIN_FILTER, osg::Texture3D::LINEAR);
	texture3D->setFilter(osg::Texture3D::MAG_FILTER, osg::Texture3D::LINEAR);
	//设置环绕模式
	texture3D->setWrap(osg::Texture3D::WRAP_R, osg::Texture3D::REPEAT);
	//关联三维纹理图像数据
	texture3D->setImage(image_3d.get());

	//设置自动生成纹理坐标
	osg::ref_ptr<osg::TexGen> texgen = new osg::TexGen;
	//设置自动生成纹理坐标为视觉线性
	texgen->setMode(osg::TexGen::OBJECT_LINEAR);
	//指定参考平面
	texgen->setPlane(osg::TexGen::R, osg::Plane(1.0f, 0.0f, 0.0f, 0.2f));

	//创建状态属性对象
	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
	//设置在R上自动生成纹理坐标
	stateset->setTextureMode(0, GL_TEXTURE_GEN_R, osg::StateAttribute::ON);
	//启用自动生成纹理坐标
	stateset->setTextureAttribute(0, texgen.get());
	//启用三维纹理对象
	stateset->setTextureAttributeAndModes(0, texture3D.get(), osg::StateAttribute::ON);

	return stateset.get();
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> root = new osg::Group();

	osg::ref_ptr<osg::Node> node = createNode();

	//创建状态属性对象
	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
	stateset = createState();

	//使用三维纹理
	node->setStateSet(stateset.get());

	root->addChild(node.get());

	//优化场景数据
	osgUtil::Optimizer optimzer;
	optimzer.optimize(root.get());

	//方便查看在多边形之间切换,以查看三角网
	viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
	viewer->addEventHandler(new osgViewer::StatsHandler());
	viewer->addEventHandler(new osgViewer::WindowSizeHandler());
	viewer->setSceneData(root.get());
	viewer->setUpViewInWindow(600, 600, 1000, 800);

	return viewer->run();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1273163.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识蒸馏代码实现(以MNIST手写数字体为例,自定义MLP网络做为教师和学生网络)

dataloader_tools.py import torchvision from torchvision import transforms from torch.utils.data import DataLoaderdef load_data():# 载入MNIST训练集train_dataset torchvision.datasets.MNIST(root "../datasets/",trainTrue,transformtransforms.ToTens…

Unity 注释的方法

1、单行注释&#xff1a;使用双斜线&#xff08;//&#xff09;开始注释&#xff0c;后面跟注释内容。通常注释一个属性或者方法&#xff0c;如&#xff1a; //速度 public float Speed;//打印输出 private void DoSomething() {Debug.Log("运行了我"); } …

老师旁听公开课到底听什么

经常参加公开课是老师提升自己教学水平的一种方式。那么&#xff0c;在旁听公开课时&#xff0c;老师应该听什么呢&#xff1f; 听课堂氛围 一堂好的公开课&#xff0c;应该能够让学生积极参与&#xff0c;课堂气氛活跃&#xff0c;而不是老师一个人唱独角戏。如果老师能够引导…

第16关 革新云计算:如何利用弹性容器与托管K8S实现极速服务POD扩缩容

------> 课程视频同步分享在今日头条和B站 天下武功&#xff0c;唯快不破&#xff01; 大家好&#xff0c;我是博哥爱运维。这节课给大家讲下云平台的弹性容器实例怎么结合其托管K8S&#xff0c;使用混合服务架构&#xff0c;带来极致扩缩容快感。 下面是全球主流云平台弹…

Windows系列:windows2003-建立域

windows2003-建立域 Active Directory建立DNS建立域查看日志xp 加入域 Active Directory 活动目录是一个包括文件、打印机、应用程序、服务器、域、用户账户等对象的数据库。 常见概念&#xff1a;对象、属性、容器 域组件&#xff08;Domain Component&#xff0c;DC&#x…

java操作windows系统功能案例(二)

1、打印指定文件 可以使用Java提供的Runtime类和Process类来打印指定文件。以下是一个示例代码&#xff1a; import java.io.File; import java.io.IOException;public class PrintFile {public static void main(String[] args) {if (args.length ! 1) {System.out.println(…

C# Onnx 百度飞桨开源PP-YOLOE-Plus目标检测

目录 效果 模型信息 项目 代码 下载 C# Onnx 百度飞桨开源PP-YOLOE-Plus目标检测 效果 模型信息 Inputs ------------------------- name&#xff1a;image tensor&#xff1a;Float[1, 3, 640, 640] name&#xff1a;scale_factor tensor&#xff1a;Float[1, 2] ----…

HuggingFace学习笔记--Model的使用

1--Model介绍 Transformer的 model 一般可以分为&#xff1a;编码器类型&#xff08;自编码&#xff09;、解码器类型&#xff08;自回归&#xff09;和编码器解码器类型&#xff08;序列到序列&#xff09;&#xff1b; Model Head&#xff08;任务头&#xff09;是在base模型…

Windows11如何让桌面图标的箭头消失(去掉快捷键箭头)

在Windows 11中&#xff0c;桌面图标的箭头是快捷方式图标的一个标志&#xff0c;用来表示该图标是一个指向文件、文件夹或程序的快捷方式。如果要隐藏这些箭头&#xff0c;你需要修改Windows注册表或使用第三方软件。 在此之前&#xff0c;我需要提醒你&#xff0c;修改注册表…

【unity实战】如何更加规范的创建各种Rogue-Lite(肉鸽)风格的物品和BUFF效果(附项目源码)

文章目录 前言定义基类实现不同的BUFF效果一、回血BUFF1. 简单的回血效果实现2. BUFF层数控制回血量 二、攻击附带火焰伤害三、治疗领域1. 简单的治疗领域实现2. 添加技能冷却时间 通过拾取物品获取对应的BUFF参考源码完结 前言 当创建各种Rogue-Lite&#xff08;肉鸽&#xf…

VS2022使用Vim按键

VS2022使用Vim按键 在插件管理里面搜索VsVim 点击安装&#xff0c;重启VS 工具->选项->VsVim 配置按键由谁处理&#xff0c;建议Ctrl C之类常用的使用VS处理&#xff0c;其它使用Vim处理

shell编程系列(7)-使用wc进行文本统计

文章目录 前言wc命令的使用wc命令的参数说明&#xff1a;统计字数统计行数打印文本行号 结语 前言 统计功能也是我们在shell编程中经常碰到的一个需求&#xff0c;wc命令可以适用于任何需要统计的数据&#xff0c;不只是统计文本&#xff0c;配合ls命令我们可以统计文件的个数…

electron调用dll问题总汇

通过一天的调试安装&#xff0c;electron调用dll成功&#xff0c;先列出当前的环境&#xff1a;node版本: 18.12.0&#xff0c;32位的&#xff08;因为dll为32位的&#xff09; VS2019 python node-gyp 1、首先要查看报错原因&#xff0c;通常在某一行会有提示&#xff0c;常…

在Linux上安装KVM虚拟机

一、搭建KVM环境 KVM&#xff08;Kernel-based Virtual Machine&#xff09;是一个基于内核的系统虚拟化模块&#xff0c;从Linux内核版本2.6.20开始&#xff0c;各大Linux发行版就已经将其集成于发行版中。KVM与Xen等虚拟化相比&#xff0c;需要硬件支持的完全虚拟化。KVM由内…

vue3 router-view 使用keep-alive报错parentcomponent.ctx.deactivate is not a function

问题 如下图&#xff0c;在component组件上添加v-if判断&#xff0c;会报错: parentcomponent.ctx.deactivate is not a function 解决方法 去除v-if&#xff0c;将key直接添加上。由于有的公用页面&#xff0c;需要刷新&#xff0c;不希望缓存&#xff0c;所以需要添加key…

2023/11/30JAVAweb学习

数组json形式 想切换实现类,只需要只在你需要的类上添加 Component 如果在同一层,可以更改扫描范围,但是不推荐这种方法 注入时存在多个同类型bean解决方式

C 中的结构 - 存储、指针、函数和自引用结构

0. 结构体的内存分配 当声明某种类型的结构变量时&#xff0c;结构成员被分配连续&#xff08;相邻&#xff09;的内存位置。 struct student{char name[20];int roll;char gender;int marks[5];} stu1; 此处&#xff0c;内存将分配给name[20]、roll、gender和marks[5]。st1这…

Redis学习文档

目录 一、概念1、特征2、关系型数据库和非关系型数据库的区别3、键的结构4、Redis的Java客户端5、缓存更新策略5.1、概念5.2、代码 6、缓存穿透6.1、含义6.2、解决办法6.3、缓存空值代码举例6.4、布隆过滤器代码举例 7、缓存击穿7.1、概念7.2、解决办法7.3、互斥锁代码举例7.4、…

卡码网语言基础课 | 17. 判断集合成员

目录 一、 set 集合 二、 创建集合 2.1 引入头文件 2.2 创建 2.3 插入元素 2.4 删除元素 三、 find的用法 四、 实现基本解题 五、 延伸拓展 题目&#xff1a;编写一个程序&#xff0c;判断给定的整数 n 是否存在于给定的集合中。 输入描述&#xff1a; 有多组测试…

Pycharm中使用matplotlib绘制动态图形

Pycharm中使用matplotlib绘制动态图形 最终效果 最近用pycharm学习D2L时发现官方在jupyter notebook交互式环境中能动态绘制图形&#xff0c;但是在pycharm脚本环境中只会在最终 plt.show() 后输出一张静态图像。于是有了下面这段自己折腾了一下午的代码&#xff0c;用来在pych…