OSG数学+基本图元/几何体+OpenGL方式绘制——b站OSG72讲5-6节(Part2)

三、基本图元(几何体绘制)

6.使用OSG中预定义的几何体

osg::Shape类

直接继承与osg::Object
在这里插入图片描述
osg::Shape类是各种内嵌几何体的基类,它不但可用于剔除和碰撞检测,还可用于生成预定义的几何体对象。

常用内嵌几何体包括:
正方体:osg::Box
太空舱:osg::Capsule
椎体:osg::Cone
柱体:osg::Cylinder
高度图:osg::HeightField
无限平面:osg::InfinitePlane
球体:osg::Sphere
三角片:osg::TriangleMesh

在这里插入图片描述

osg::ShapeDrawable类

如果要渲染内嵌几何体,就必须将其与osg::Drawable关联,可以使用osg::ShapeDrawable的构造函数来完成这个功能:

ShapeDrawable(Shape* shape, TessellationHints* hints=0);
// 第一个参数为shape,第二个参数默认下不细化

在这里插入图片描述

网格化类osg::TessellationHints/精度

在这里插入图片描述
主要用于设置预定义几何体对象的精细程度,精细程度越高,表示其细分越详细,但是对于不同预定义几何体对象它的作用是不一样的:
在这里插入图片描述

实例:创建预定义几何体createShape

...
osg::ref_ptr<osg::Geode> CreateShape()
{
	osg::ref_ptr<osg::Geode> geode = new osg::Geode;
	// 申请纹理和图片对象
	osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
	osg::ref_ptr<osg::Image> image/*= new osg::Image*/;
	// 申请材质
	osg::ref_ptr<osg::Material> material = new osg::Material;
	// 申请精细度对象,精细度越高,细分就越多
	osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints;



	// 启动透明度
	geode->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
	// 启动深度测试
	geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
	// 设置纹理
	geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
	// 设置材质
	geode->getOrCreateStateSet()->setAttributeAndModes(material.get(), osg::StateAttribute::ON);




	// 定义半径和高度
	float radius = 0.2f;
	// 创建一个球体,第一个参数是预定义几何体对象,第二个是精细度,默认为0;osg::ShapeDrawable派生于osg::Geometry
	geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0, 0.0, 0.0), radius), hints));
	// 创建一个立方体
	geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(1.0, 0.0, 0.0), 1.0, 1.0, 1.0), hints));



	// 精细度
	hints->setDetailRatio(1.0f);
	// 纹理
	image = osgDB::readImageFile("melody.jpg");
	if (image.valid())
	{
		texture->setImage(image.get());
	}
	// 材质
	material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 0.1));
	material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 0.1));
	material->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 0.1));
	material->setShininess(osg::Material::FRONT_AND_BACK, 60.0);


	

	return geode.get();
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
	osg::ref_ptr<osg::Group> group = new osg::Group;
	
	group->addChild(osgDB::readNodeFile("D:\\vs2019 64位 3rdParty osg365 oe32\\OpenSceneGraph-Data\\glider.osg"));
	group->addChild(CreateShape());

	viewer->setSceneData(group.get());
	return viewer->run();
}

其中加载纹理部分发现readImageFile读取失误,原因:
1.OSG插件问题,因为需要使用很多第三方库的,最好去这里
,去那里下载已经编译好且配套的osg+第三方库。
2.Debug不能加载,但是Release能加载,是因为需要将“osg365\bin\osgPlugins-3.6.5\osgdb_jpegd.dll”放到.exe目录下(纹理我用的.jpg图片)。

  1. openGL中想要blend(混合测试)有预期的效果,必须先渲染所有不透明的,再渲染深度值从大到小的透明的!这样子才能通过深度测试。
  2. 但是osg可以通过以下方法设置透明

7.位图的显示(主要是封装glDrawPixels)

图像与图像的绘制

在场景中绘制位图的工作可以通过封装glDrawPixels来完成,这个函数的通常用法如下:
在这里插入图片描述

  • osg::Image保存位图数据类,比像素数组更安全
    在这里插入图片描述
    在这里插入图片描述
  • 分辨率:
    在这里插入图片描述
  • 纹理格式变量getInternalTextureFormat在图像作为纹理使用时,可以用来决定纹理的内部格式。
  • 图像的像素格式变量getPixelFormat指示了像素数据的类型和读取方式,如下图:
    在这里插入图片描述
  • 图像的数据类型变量getDataTyle表达了每个分量的类型信息,如下:
    在这里插入图片描述
  • 其实很多可以和glTexImage2D等函数中同名参数有同样的对应关系,可以作为参考。
    在这里插入图片描述
  • osg::Drawable::DrawPixels类,封装了glDrawPixels功能(参考)
    在这里插入图片描述
    函数实现的重点无疑是Drawable类的drawImplementation和computeBound:
    在这里插入图片描述
    在这里插入图片描述
    重载了drawImplementation之后,该类同样可以选择是否使用显示列表来保存其绘制指令;而computeBound的重载保证了该类能够正确地加入到场景的保卫体层次BVH中。

实例:在场景中绘制位图

int main(int argc, char* argv[])
{
    osg::ref_ptr<osg::DrawPixels> bitmap = new osg::DrawPixels;
    bitmap->setPosition(osg::Vec3(0,0,0));
    bitmap->setImage(osgDB::readImageFile("C:\\Users\\Administrator\\Desktop\\cin.png"));


    osg::ref_ptr<osg::DrawPixels> bitmap2 = new osg::DrawPixels;
    bitmap2->setPosition(osg::Vec3(80, 0, 0));
    bitmap2->setImage(osgDB::readImageFile("C:\\Users\\Administrator\\Desktop\\cin.png"));


    osg::ref_ptr<osg::DrawPixels> bitmap23 = new osg::DrawPixels;
    bitmap23->setPosition(osg::Vec3(200, 0, 0));
    bitmap23->setImage(osgDB::readImageFile("C:\\Users\\Administrator\\Desktop\\cin.png"));
    bitmap23->setSubImageDimensions(64,64,128,128);
    bitmap23->setUseSubImage(true);

    osg::ref_ptr<osg::Geode> geo = new osg::Geode;
    geo->addDrawable(bitmap.get());
    geo->addDrawable(bitmap2.get());
    geo->addDrawable(bitmap23.get());

    osg::ref_ptr<osgViewer::Viewer> v = new osgViewer::Viewer;
    v->setSceneData(geo.get());
    return v->run();
}

10.文字的显示

相比于使用位图来显示文字,使用纹理来表达矢量类型的文字更好,优点在于每个字型都是使用数学公式来描述的,并使用光滑的曲线实现笔画之间的链接。
因此只要预先设置的纹理分辨率满足需求,那么对纹理面进行放大与缩小,或者改变用户的视点时,都不会造成明显的文字失真变形。
在这里插入图片描述

  • osgText::Text类 :osg::Drawable,该类重载了drawImplementation
    在这里插入图片描述
  • osgText::Text3D 使用几何体数据而不是纹理图片
    提供了三维文字的表达手段,除了不具备直接设置颜色和设置背景阴影,还多了别的成员:
    在这里插入图片描述
  • Text::drawImplementation内部实现爱你关键在于将字库中的指定文字取出,判断他们的形状(例如矢量笔画或者点阵位图),并转换、合并为贴在平面上的二维纹理。
  • osgText::Font
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    osgText::Font *font = osgText::readFontFile("fonts/arial.ttf");
    在这里插入图片描述
  • osgText::String 完成Ude编码的保存和现实
    在这里插入图片描述

实例:一首古诗/多字节字符文本转换宽字符文本数据


#include <osg/Geode>
#include <osg/Geometry>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <locale.h>

void setupProperties(osgText::Text& textObject, osgText::Font* font,
    float size, const osg::Vec3& pos)
{
    textObject.setFont(font);
    textObject.setCharacterSize(size);
    textObject.setPosition(pos);
    textObject.setColor(osg::Vec4(0.0, 0.0, 1.0, 1.0));
    textObject.setAlignment(osgText::Text::CENTER_BOTTOM);
    textObject.setAxisAlignment(osgText::Text::XZ_PLANE);
}

void createContent(osgText::Text& textObject, const char* string)
{
    // 转宽字符
    int requiredSize = mbstowcs(NULL, string, 0);
    wchar_t* wtext = new wchar_t[requiredSize + 1];
    mbstowcs(wtext, string, requiredSize + 1);

    //
    textObject.setText(wtext);
    delete wtext;
}

int main(int argc, char** argv)
{
    // 指定本地计算机编码格式,.936表示中国
    // 根据不同的编码格式,方能正确执行多字节字符文本/字符串宽字符转换工作
    setlocale(LC_ALL, ".936");

    //
    const char* titleString = "木兰辞\n拟古决绝词柬友";
    const char* textString = {
        "人生若只如初见,何事秋风悲画扇;\n"
        "等闲变却故人心,却道故人心易变。\n"
        "骊山语罢清宵半,夜雨霖铃终不怨;\n"
        "何如薄幸锦衣郎,比翼连枝当日愿。"
    };

    osgText::Font* fontHei = osgText::readFontFile("Fonts/simhei.ttf");
    osgText::Font* fontKai = osgText::readFontFile("Fonts/simkai.ttf");

    osg::ref_ptr<osgText::Text> title = new osgText::Text;
    setupProperties(*title, fontHei, 20.0f, osg::Vec3(0.0f, 0.0f, 0.0f));
    createContent(*title, titleString);

    osg::ref_ptr<osgText::Text> text = new osgText::Text;
    setupProperties(*text, fontKai, 15.0f, osg::Vec3(0.0f, 0.0f, -80.0f));
    createContent(*text, textString);

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable(osg::createTexturedQuadGeometry(
        osg::Vec3(-150.0, 1.0, -130.0), osg::Vec3(300.0, 0.0, 0.0), osg::Vec3(0.0, 0.0, 200.0), 1.0, 1.0));
    geode->addDrawable(title.get());
    geode->addDrawable(text.get());

    osgViewer::Viewer viewer;
    viewer.setSceneData(geode.get());
    return viewer.run();
}

9.多边形分格化 osgUtil::Tessellator

OpenGL为了快速渲染,只能直接显示简单凸多边形(多边形上任意两点的连线上的点属于该多边形,对凹多边形或者自交叉多边形的渲染结果将不确定),为了正确显示凹多边形或者自交叉多边形,就必须把它们分解为简单的凸多边形,成为多边形的分格化
其实和网上为了正确渲染,使用三角化算法的目的差不多。

分格化步骤:
1.创建多边形分格化对象Tessellator
2.设置分格化对象的类型:
TESS_TYPE_GEOMETRY///分格化几何体
TESS_TYPE_DRAWABLE///分格化几何体中的Drawable(如多边形,三角形,四边形等)
TESS_TYPE_POLYGONS///只分格化几何体中的多边形
3.根据计算的环绕数指定相应的环绕规则
环绕数参考《OpenGL编程指南》,逆时针为正,顺势正为负
在这里插入图片描述

环绕规则/选择渲染区域:
TESS_WINDING_ODD = GLU_TESS_WINDING_ODD/// 环绕数为奇数
TESS_WINDING_NONZERO = GLU_TESS_WINDING_NONZERO///环绕数为非零数
TESS_WINDING_POSITIVE = GLU_TESS_WINDING_POSITIVE///环绕数为正数
TESS_WINDING_NEGATIVE = GLU_TESS_WINDING_NEGATIVE///环绕数为负数
TESS_WINDING_ABS_GEQ_TWO = GLU_TESS_WINDING_ABS_GEQ_TWO///环绕数为绝对值大于或等于2

示例

// 使用分格化绘制凹多边形
osg::ref_ptr<osg::Geode> tesslatorGeometry()
{
	osg::ref_ptr<osg::Geode> geode= new osg::Geode();
	
	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
	geode->addDrawable(geom.get());

	// 顶点数据
	//墙
	const float wall[5][3] = 
	{
		{ 2200.0f, 0.0f, 1130.0f },
		{ 2600.0f, 0.0f, 1130.0f },
		{ 2600.0f, 0.0f, 1340.0f },
		{ 2400.0f, 0.0f, 1440.0f },
		{ 2200.0f, 0.0f, 1340.0f }
	};
 
	//门
	const float door[4][3] = 
	{
		{ 2360.0f, 0.0f, 1130.0f },
		{ 2440.0f, 0.0f, 1130.0f },
		{ 2440.0f, 0.0f, 1230.0f },
		{ 2360.0f, 0.0f, 1230.0f }
	};
 
	//四扇窗口
	const float windows[16][3] = 
	{
		{ 2240.0f, 0.0f, 1180.0f },
		{ 2330.0f, 0.0f, 1180.0f },
		{ 2330.0f, 0.0f, 1220.0f },
		{ 2240.0f, 0.0f, 1220.0f },
 
		{ 2460.0f, 0.0f, 1180.0f },
		{ 2560.0f, 0.0f, 1180.0f },
		{ 2560.0f, 0.0f, 1220.0f },
		{ 2460.0f, 0.0f, 1220.0f },
 
		{ 2240.0f, 0.0f, 1280.0f },
		{ 2330.0f, 0.0f, 1280.0f },
		{ 2330.0f, 0.0f, 1320.0f },
		{ 2240.0f, 0.0f, 1320.0f },
 
		{ 2460.0f, 0.0f, 1280.0f },
		{ 2560.0f, 0.0f, 1280.0f },
		{ 2560.0f, 0.0f, 1320.0f },
		{ 2460.0f, 0.0f, 1320.0f }
	};
	
	// 设置顶点数据
	osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array();
	geom->setVerTexArray(coords.get());
	
	// 法线
	osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array();
	normal->push_back(osg::Vec3(0.0,-1.0,0.0));
	geom->setNormalArray(normal.get());
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
	
	// 添加墙
	for (int i = 0; i < 5;++i)
	{
		coords->push_back(osg::Vec3(wall[i][0], wall[i][1], wall[i][2]));
	}
	//setVerTexArray之后,设置绘制方式,相当于OpenGL中绘制指令
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,0,5));

	//添加窗
	for (int i = 0; i < 16; ++i)
	{
		coords->push_back(osg::Vec3(windows[i][0], windows[i][1], windows[i][2]));
	}

	//添加门
	for (int i = 0; i < 4;++i)
	{
		coords->push_back(osg::Vec3(door[i][0], door[i][1], door[i][2]));
	}
	
	//门窗的绘制方式,四边形,从顶点数据的第5个数据开始,
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 5, 20));
	
	// 创建分格化对象
	osg::ref_ptr<osgUtil::Tessellator> tscx = new osgUtil::Tessellator();
	// 设置分格类型为几何体,用Geometry绘制的
	tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);
	// 因为需要填充,所以将只显示轮廓线为false
	tscx->setBoundaryOnly(false);
	// 设置环绕规则,只渲染环绕数为奇数的
	tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD);

	// 使用分格化
	tscx->retessellatePolygons(*(geom.get()));
	return geode.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::Geode> geode = tesslatorGeometry();
	root->addChild(geode.get());
	viewer->setSceneData(root.get());
	return viewer->run();
}

// 效果如下(内网截图不了,回家补上自己的图吧)
在这里插入图片描述

10.几何体操作

场景都是由基本的绘图基元构成的,基本的绘图基元构成简单的几何体,简单的几何体构成复杂的几何体,复杂的几何体最终构造成复杂的场景。
当多个几何体组合时,可能存在多种降低场景渲染效率的原因,但是一定的几何体操作可以在相当程度上可以提高渲染效率。

简化几何体osgUtil::Simplifier

在这里插入图片描述
才用访问器的方式遍历几何体并对其进行简化处理,可以直接调用下面的成员函数:

virtual void apply (osg::Geode &geode);//不同版本osg参数可能不同!
void simplify (osg::Geometry &geometry);///简化几何体

simplify使用方法可以参考本文“三角带绘制osgUtil::TriStripVisitor(条带化)示例”

这两个函数同样可用于osg::Node节点,不过关联示例时需要使用accept()方法。

示例1——accept():

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

	// 设置样本比率,样本比率越大,简化越小
	// 样本比率越小,简化越多
	float sampleRatio = 0.1f;
	// 设置点的最大误差
	float maxError = 4.0f;

	// 创建简化对象
	osgUtil::Simplifier simplifier(sampleRatio,maxError);
	// 读取牛的模型
	osg::ref_ptr<osg::Node> node1 = osgDB::readNodeFile("cow.osg");
	// 深拷贝牛的模型到node2节点
	osg::ref_ptr<osg::Node> node2 = (osg::Node*)(node1->clone(osg::CopyOp::DEEP_COPY_ALL));
	
	// 变换节点
	osg:ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
	pat->setPosition(osg::Vec3(10.0,0.0,0.0));
	pat->addChild(node2.get());
	// 简化处理
	pat->accept(simplifier);
	
	// 切换网格模式,方便比较模型
	osg::ref_ptr<osgFX::Scribe> scribe = new osgFX::Scribe();
	scribe->addChild(node1.get());
	scribe->addChild(pat.get());

	root->addChild(scribe.get());
	viewer->setSceneData(root.get());
	viewer->run();
}

Delaunay三角网绘制osgUtil::DelaunayTriangulator(三维重建/三角网)

还有个四角面片(osg::HeightField)
参考这里,可以知道和Tessellator的区别

TIN模型在数字地形建模中,不规则三角网TIN通过从不规则离散分布的数据点生成的连续三角面来逼近地形表面。
TIN模型基本要求:
1.TIN是唯一的
2.力求最佳三角形几何形状
3.保证最邻近的点构成三角形
而其中Delaunay三角网在地形拟合方面表现最为出色,常用于TIN的生成。

在这里插入图片描述

创建Delaunay三角网步骤:
1.创建顶点数组
2.创建一个osgUtil::DelaunayTriangulator对象,并初始化顶点数组,同时生成三角网,代码如下:
bool DelaunayTriangulator::triangulate ();
3.创建一个几何体对象,设置顶点数组,把osgUtil::DelaunayTriangulator类对象生成的绘制图元/绘图基元(osg::DrawElementsUInt * DelaunayTriangulator::getTriangles (),相当于索引数组!)加入到几何体中,在生成Delaunay三角网时,读者还可以添加一些限制条件,限制条件可以是点、线或多边形,例如:
void DelaunayTriangulator::addInputConstraint (DelaunayConstraint *dc);

其实就是三维无序离散点构成三角网
示例:

#include "包含离散点.h"
int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
	osg::ref_ptr<osg::Group> root = new osg::Group();
	
	// 创建顶点数组
	osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array;
	
	// 添加顶点数据
	for(unsigned int i = 0;i<n;i++)
	{
		coords->push_back(osg::Vec3(vertex[i][0],vertex[i][1],vertex[i][2]));
	}
	
	// 创建Delaunay三角网对象
	osg::ref_ptr<osgUtil::DelaunayTriangulator> dt = new osgUtil::DelaunayTriangulator(coords.get());
	// 生成三角网
	dt->triangulate();

	// 创建几何体
	osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
	// 设置顶点数组
	geometry->setVertexArray(coords.get());
	
	// 加入绘图基元
	geometry->addPrimitiveSet(dt->getTriangles());
	
	// f
	osg::ref_ptr<osgFX::Scribe> scribe = new osgFX::Scribe();
	scribe->addChild(geometry.get());
	
	root->addChild(scribe.get());
	viewer->setSceneData(scribe.get());
	viewer->run();
}

三角带绘制osgUtil::TriStripVisitor(条带化)

在这里插入图片描述
采用访问器的机制遍历场景中的几何体,实现三角带绘制,以提高渲染效率。

大多数图形卡不支持三角网,在渲染三角形时,一般是将3个顶点同时提交,可能会重复提交顶点,所以很多API和硬件支持特殊的三角网格式以减少传输量。
而三角带是一种在理想状态下,三角带可以用N+2个顶点表示存储N个面。虽然一般网格需要一个以上的三角带来表达,但是还是尽量使三角带数目更少,因为建立各个三角带需要额外的处理时间。

/*
条带化几何体
Convert mesh primitives in Geometry into Tri Strips.
Converts all primitive types except points and lines, linestrips which it leaves unchanged.
*/
void osgUtil::TriStripVisitor::stripify	(osg::Geometry& drawable);

//应用于叶节点
virtual void osgUtil::TriStripVisitor::apply(osg::Geode& geode);

参考osg 绘制使用的OpenGL的绘制过程
在这里插入图片描述

这两个函数同样可用于osg::Node节点,不过关联示例时需要使用accept()方法。可以参考本文的简化几何体osgUtil::Simplifier示例o( ̄▽ ̄)ブ。

// 创建四边形节点
osg::ref_ptr<osg::Geometry> createQuad()
{
	//
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();
	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

	// 创建顶点数组,注意顶点的添加顺序是逆时针的
	osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
	v->push_back(osg::Vec3(0.0,0.0,0.0));
	v->push_back(osg::Vec3(1.0,0.0,0.0));
	v->push_back(osg::Vec3(1.0,0.0,1.0));
	v->push_back(osg::Vec3(0.0,0.0,1.0));
	geom->setVertexArray(v.get());

	// 创建纹理坐标
	osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
	vt->push_back(osg::Vec2(0.0,0.0));
	vt->push_back(osg::Vec2(1.0,0.0));
	vt->push_back(osg::Vec2(1.0,1.0));
	vt->push_back(osg::Vec2(0.0,1.0));
	geom->setTexCoordArray(0,vt.get());
	
	// 创建颜色数组
	osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();
	vc->push_back(osg::Vec4(1.0,0.0,0.0,1.0));
	vc->push_back(osg::Vec4(0.0,1.0,0.0,1.0));
	vc->push_back(osg::Vec4(0.0,0.0,1.0,1.0));
	vc->push_back(osg::Vec4(1.0,1.0,0.0,1.0));
	geom->setColorArray(vc.get());
	geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

	// 法线
	osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
	nc->push_back(osg::Vec3(0.0,-1.0,0.0));
	geom->setNormalArray(nc.get());
	geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

	// 添加绘图基元,指定绘制指令
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
	return geom.get();
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
	osg::ref_ptr<osg::Group> root = new osg::Group();
	osg::ref_ptr<osgFX::Scribe> scribe = new osgFX::Scribe();
	scribe->addChild(root.get());

	//
	osg::ref_ptr<osg::Geometry> geom = createQuad();
	// 对几何体进行条带化
	osgUtil::TriStripVisitor stripper;
	stripper.stripify(*(geom.get()));
	root->addChild(geom.get()); 
	viewer->setSceneData(scribe .get());
	viewer->run();
}

生成顶点法向量osgUtil::SmoothingVistor(生成法线)

该类可以生成顶点法线,也就是所用共享此顶点的面的法线的平均值。

// 用于生成顶点法向量
static void smooth(osg::Geometry &geoset);

法向量的用途:

  • 计算光照
  • 背面剔除
  • 模拟粒子系统在表面的弹跳效果
  • 通过只需要正面而加速碰撞检测
    当三角形的顶点按照逆时针排序,通过叉乘就可以计算外表面的法向量。
    本类使用的是平均三角形法向量求得顶点法向量,但是不具有通用性,例如使用布告板时,生成顶点法线的方法就会失效,因为布告板由两个三角形背靠构成,它的两个法向量时相反的,其平均值为0,所以不能初始化,可以采用双面三角形方法。

计算逐像素法线(计算当前面法线)

fwidth、dFdx/dFdy 是 GLSL 中的函数方法:
dFdx(v) = 该像素点右边的v值 - 该像素点的v值 // v 可以是任意值
dFdy(v) = 该像素点下面的v值 - 该像素点的v值
fwidth(v) = abs( dFdx(v) + dFdy(v))
fwidth、dFdx/dFdy 只在片元/片段着色中可以使用
fwidth、dFdx/dFdy 计算的是逐像素的变量差值,而不是逐片元
fwidth、dFdx/dFdy 接收什么类型的参数,返回值就是什么类型的参数
fwidth、dFdx/dFdy 参数可以是任意参数,但应该是从顶点着色器中传递的参数(varying)
假如传入的参数为一个固定的值,那么像素差值为 “0”,因为所有像素输出的值都一样

vec3 normal = normalize(  cross(dFdx(pos),  dFdy(pos))  );

示例:

// 创建四边形节点,不设定法向量
osg::ref_ptr<osg::Geometry> createQuad()
{
	//
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();
	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

	// 创建顶点数组,注意顶点的添加顺序是逆时针的
	osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
	v->push_back(osg::Vec3(0.0,0.0,0.0));
	v->push_back(osg::Vec3(1.0,0.0,0.0));
	v->push_back(osg::Vec3(1.0,0.0,1.0));
	v->push_back(osg::Vec3(0.0,0.0,1.0));
	geom->setVertexArray(v.get());

	// 创建纹理坐标
	osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
	vt->push_back(osg::Vec2(0.0,0.0));
	vt->push_back(osg::Vec2(1.0,0.0));
	vt->push_back(osg::Vec2(1.0,1.0));
	vt->push_back(osg::Vec2(0.0,1.0));
	geom->setTexCoordArray(0,vt.get());
	
	// 创建颜色数组
	osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();
	vc->push_back(osg::Vec4(1.0,0.0,0.0,1.0));
	vc->push_back(osg::Vec4(0.0,1.0,0.0,1.0));
	vc->push_back(osg::Vec4(0.0,0.0,1.0,1.0));
	vc->push_back(osg::Vec4(1.0,1.0,0.0,1.0));
	geom->setColorArray(vc.get());
	geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);

	// 添加绘图基元,指定绘制指令
	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
	return geom.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::Geometry> geom = createQuad();

	// 生成顶点法线
	osgUtil::SmoothingVisitor::smooth(*(geom.get()));
	root->addChild(geom.get());
	viewer->setSceneData(root.get());
	viewer->run();
}
  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值