文章目录
三、基本图元(几何体绘制)
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图片)。
- openGL中想要blend(混合测试)有预期的效果,必须先渲染所有不透明的,再渲染深度值从大到小的透明的!这样子才能通过深度测试。
- 但是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::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();
}