首页 > 其他分享 >三维几何生成:多段线、圆弧

三维几何生成:多段线、圆弧

时间:2024-08-21 22:37:02浏览次数:8  
标签:curSegmentSize float back 三维 圆弧 push 多段 sides osg

一、三维空间多段线几何

1 应用背景

​​  opengl常用glLineWidth命令设置线宽,此线宽在透视投影中不会随着相机远近变化而缩放。项目中高版本glLineWidth命令失效,需要考虑如何快速、方便、宽度不变的多段线几何。方案a:纯shader绘制曲线,绘制到一个二维平面上,然后将平面旋转朝向屏幕,保持平面缩放不变,实现中需要考虑绘制顺序,因为失去了深度,没办法做深度测试。方案b:传前一个点和后一个点到顶点属性中去,使用几何着色器,将线宽对应的新顶点发送出去,实现起来还是比较二维,从另外的视角观察直线会被折叠。方案c:手动生成每一线段几何面片,组合所有面片为一个drawable,实现中遇到有线段连接处不光滑、固定线宽等问题,总体上基本和glLineWidth绘制的线效果一致。ps:根据国外博客学到了方案4,实例化绘制线段技术,实现起来效果很不错,优法!

2 单个线段几何生成

​​  如上图所示,三维线段几何可以抽象为圆柱体,圆柱体的半径为线宽,高度为上下底面M和N构成的向量MN长度之差。注意到圆柱体进一步离散化为多面体,我们只需要侧边包围面,将其展开就得到了长方形。长方形的长为底面周长,高为MN长度。注意到,多段线的线段间即有平移也有旋转,几何坐标要乘以旋转矩阵。

void constructSegment(osg::Vec3 firstPt,osg::Vec3 secondPt,float width)
{
	osg::ref_ptr<osg::Vec3Array>  vertices=new osg::Vec3Array;
	osg::ref_ptr<osg::DrawElementsUInt>  sides=new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_FAN);
	int numSegments=10;
	float radius=0.5*width;
	auto center=(firstPt+secondPt)*0.5;
	float height=(firstPt-secondPt).length();
	
	float basez=-height*0.5f;
	float topz=height*0.5f;
	
	
	auto dir=firstPt-secondPt;
	osg::Quat rotQuat;
	rotQuat.makeRotate(osg::Z_AXIS,dir);
	
	float angle=0.0f;
	float angleDelta=2.f*M_PI/(float)numSegments;
	
	for(int bodyi=0;bodyi<numSegments;++bodyi,angle+=angleDelta)
	{
		float c=cosf(angle);
		float s=sinf(angle);
		auto P=osg::Vec3(c*radius,s*radius,topz);
		auto Q=osg::Vec3(c*radius,s*radius,topz);
		P=P*osg::Matrix:;rotate(rotQuat)*osg::Matrix::translate(center);
		Q=Q*osg::Matrix:;rotate(rotQuat)*osg::Matrix::translate(center);
		vertices->push_back(P);
		vertices->push_back(Q);		
	}
	//添加圆柱索引
	float curSegmentSize=0;
	for(int i=0;i<numSegments*2-2;i+=2)
	{
		sides->push_back(curSegmentSize+i);
		sides->push_back(curSegmentSize+i+1);
		sides->push_back(curSegmentSize+i+3);
		sides->push_back(curSegmentSize+i+2);
		sides->push_back(curSegmentSize+i);
		sides->push_back(curSegmentSize+i+3);
	}
	sides->push_back(curSegmentSize+numSegments*2-2);
    sides->push_back(curSegmentSize+numSegments*2-1);
    sides->push_back(curSegmentSize+1);
    sides->push_back(curSegmentSize);
    sides->push_back(curSegmentSize+numSegments*2-2);
    sides->push_back(curSegmentSize+1);
}

​​  优化:按照圆柱的方式,生成的多段线几何之间的连接不光滑,且会随着相机缩放而缩放,因此通常在两段线之间添加半圆球过渡。

void constructHalfSphere(osg::ref_ptr<osg::Vec3Array>  vertices,osg::ref_ptr<osg::DrawElementsUInt>  sides,osg::Vec3 firstPt,osg::Vec3 secondPt,float radius,int&curSegmentSize,bool isTop)
{
	int numRows=5;
	int numSegments=5;
	float lDelta=M_PI/(float)numRows;
	float vDelta=1.f/(float)numRows;
	float angleDelta=M_PI*2.f/(float)numSegments;
	
	float lBase=-M_PI*0.5f+(isTop?(lDelta*(numRows/2)):0.f);
	float rBase=isTop?(cosf(lBase)*radius):0.f;
	float zBase=isTop?(sinf(lBase)*radius):-radius;
	float vBase=isTop?(vDelta*(numRows/2)):0.f;
	float nzBase=isTop?(sinf(lBase)):-1.f;
	float nRatioBase=isTop?(cosf(lBase)):0.f;
	
	int  rowbegin=isTop:numRows/2:0;
	int  rowend=isTop?numRows:numRows/2;
	
	auto dir=(firstPt-secondPt);
	osg::Quat rotQuat;
	rotQuat.makeRoate(osg::Z_AXIS,dir);
	for(int rowi=rowbegin;rowi<rowend;++rowi)
	{
		float lTop=lBase+lDelta;
		float rTop=cosf(lTop)*radius;
		float zTop=sinf(lTop)*radius;
		float vTop=vBase+vDelta;
		float nzTop=sinf(lTop);
		float nRatioTop=cosf(lTop);
		
		float angle=0.0f;
		for(int topi=0;topi<numSegments;++topi,angle+=angleDelta)
		{
			float c=cosf(angle);
			float s=sinf(angle);
			auto P=osg::Vec3(c*rTop,s*rTop,zTop);
			auto Q=osg::Vec3(c*rBase,s*rBase,zBase);
			P=P*osg::Matrixd::Rotate(rotQuat)*osg::Matrix::translate(isTop?firstPt:secondPt);
			Q=Q*osg::Matrixd::Rotate(rotQuat)*osg::Matrix::translate(isTop?firstPt:secondPt);
			vertices->push_back(P);
			vertices->push_back(Q);
		}
		//添加圆柱索引
		for(int i=0;i<numSegments*2-2;i+=2)
		{
			sides->push_back(curSegmentSize+i);
			sides->push_back(curSegmentSize+i+1);
			sides->push_back(curSegmentSize+i+3);
			sides->push_back(curSegmentSize+i+2);
			sides->push_back(curSegmentSize+i);
			sides->push_back(curSegmentSize+i+3);
		}
		sides->push_back(curSegmentSize+numSegments*2-2);
        sides->push_back(curSegmentSize+numSegments*2-1);
        sides->push_back(curSegmentSize+1);
        sides->push_back(curSegmentSize);
        sides->push_back(curSegmentSize+numSegments*2-2);
        sides->push_back(curSegmentSize+1);
        curSegmentSize+=numSegments*2;
        
        lBase=lTop;
        rBase=rTop;
        zBase=zTop;
        vBase=vTop;
        nzBase=nzTop;
        nRatioBase=nRatioTop;
	}
	return true;
}

3 实例化渲染线条技术

3.1 实例化技术简介

​​  当你要绘制大量重复的几何时,如果通过绑定vbo、上传顶点、调用绘制命令的方式会导致帧率急剧下降而卡顿。因为与GPU绘制本身相比,切换上下文,从CPU到GPU慢得多。实例化技术就是使用一个绘制函数,让GPU利用这些数据绘制多个物体,节省了每次绘制物体时CPU到GPU的通信。opengl中调用命令为:glDrawArraysInstanced和glDrawElementsInstanced。

​​  首先考虑绘制单个线段,对于三维线段,可以传递线段起点、终点坐标,以及线段顶点为图元属性,然后在顶点着色器中将矩形图元顶点坐标变换到合适位置。如下图所示,已知A、B世界坐标,先求得AB的方向向量沿x轴,然后再求沿y轴的垂直向量,依次计算两个三角形图元的顶点坐标。

std::vector<osg::Vec3> linePoints;
{
	///创建四边形顶点
	auto vertices=new osg::Vec3Array;
	vertices->push_back(osg::Vec3(0,-0.5,0));
	vertices->push_back(osg::Vec3(0,-0.5,1));
	vertices->push_back(osg::Vec3(0,0.5,1));
	vertices->push_back(osg::Vec3(0,-0.5,0));
	vertices->push_back(osg::Vec3(0,0.5,1));
	vertices->push_back(osg::Vec3(0,0.5,0));
	///创建几何起点和终点
	auto pointAs=new osg::Vec3Array;
	auto pointBs=new osg::Vec3Array;
	pointAs->push_back(linePoints.front());
	for(int i=1;i<=linePoints.size()-1;i++)
	{
		pointAs->push_back(linePoints[i]);
		pointBs->push_back(linePoints[i]);
	}
	pointBs->push_back(linePoints.back());
	///创建四边形几何
	osg::ref_ptr<osg::Geometry> quad=new osg::Geometry;
	quad->setVertexAttribArray(0,vertices,osg::Array::BIND_PER_VERTEX);
	quad->setVertexAttribArray(1,pointAs,osg::Array::BIND_PER_VERTEX);
	quad->setVertexAttribArray(2,pointBs,osg::Array::BIND_PER_VERTEX);
	quad->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES,0,6,pointAs->size()));
	quad->setusedisplayList(false);
	quad->setUseVertexBufferObjects(true);
	quad->getOrCreateStateSet()->setAttribute(new osg::VertexAttribDivisor(1,1));
	quad->getOrCreateStateSet()->setAttribute(new osg::VertexAttribDivisor(2,1));
	
	osg::ref_ptr<osg::Geode> geode=new osg::Geode;
	geode->addDrawable(quad);
	
	const char* vertCode=R"(
		#version 330 core
		layout(location=0) in vec3 aPos;
		layout(location=1) in vec3 aPointA;
		layout(location=2) in vec3 aPointB;
		uniform mat4 osg_ModelViewProjectionMatrix;
		uniform mat4 osg_ModelViewMatrix;
		uniform mat4 osg_NormalMatrix;
		uniform vec2 resolution;
		uniform float width=5.0;
		void main(){
			vec4 clip0=osg_ModelViewProjectionMatrix*vec4(aPointA,1.0);
			vec4 clip1=osg_ModelViewProjectionMatrix*vec4(aPointB,1.0);
			vec2 screen0=resolution*(0.5*clip0.xy/clip0.w+0.5);
			vec2 screen1=resolution*(0.5*clip1.xy/clip1.w+0.5);
			vec2 xBasis=normalize(screen1.xy-screen0.xy);
			float length=length(screen1.xy-screen0.xy);
			vec2 yBasis=vec2(-xBasis.y,xBasis.x);
			vec2 pt=screen0+lenthAB*xBasis*aPos.z+width*yBasis*aPos.y;
			gl_Position=vec4(clip0.w*(2.0*pt/resolution-1.0),clip0.z,clip0.w);
		}	
	)";
	const char* fragCode=R"(
		uniform vec3 front;
		uniform float opacity;
		out vec4 fragColor;
		void main()
		{
			fragColor=vec4(front,opacity);
		}
	)";

	osg::ref_ptr<osg::Shader> vertShader=new osg::Shader(osg::Shader::VERTEX,vertCode);
    osg::ref_ptr<osg::Shader> fragShader=new osg::Shader(osg::Shader::FRAGMENT,fragCode);
    osg::ref_ptr<osg::Program>  program=new osg::Program;
    program->addShader(vertShader);
    program->addShader(fragShader);
    geode->getOrCreateStateSet()->setAttributeAndModes(program,OVERRIDE_ON);
}

二、三维空间圆弧面几何

1 应用背景

​​  项目中有需求绘制三维的圆弧面几何。可以按照扇形的方式生成。

2 圆弧面几何

void constructSector(osg::Vec3 center,osg::Vec3 left,osg::Vec3 right,float radius,osg::Vec3 color,float radius,float width)
{
    ///创建扇形顶点
	osg::ref_ptr<osg::Vec3Array>  vertices=new osg::Vec3Array;
	///创建扇形法线
    osg::ref_ptr<osg::Vec3Array>  normals=new osg::Vec3Array;
    ///创建扇形拓扑
	osg::ref_ptr<osg::DrawElementsUInt>  sides=new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_FAN);
	int index=0;
	///使用球面插值计算圆弧上坐标
	auto LC=left-center;
	auto RC=right-center;
	LC.normalize();
	RC.normalize();
	osg::Quat newQuat,startQuat,endQuat;
	startQuat.makeRotate(LC,LC);
	endQuat.makeRotate(LC,RC);
	vertices->push_back(center);
	sides->push_back(index++);	
	///生成正面
	for(float i=0;i<=1;i+=0.02)
	{
		newQuat.slerp(i,startQuat,endQuat);
		auto point=center+LC*osg::Matrix(newQuat)*radius;
		vertices->push_back(point);
		normals->push_back(osg::Vec3(0,0,1));
		sides->push_back(index++);	
	}
	vertices->push_back(center);
	sides->push_back(index++);	
	///生成反面
	for(float i=1;i》=0;i-=0.02)
	{
		newQuat.slerp(i,startQuat,endQuat);
		auto point=center+LC*osg::Matrix(newQuat)*radius;
		vertices->push_back(point);
		normals->push_back(osg::Vec3(0,0,-1));
		sides->push_back(index++);	
	}
	///创建扇形几何
	 osg::ref_ptr<osg::Geometry>  sector=new osg::Geometry;
	 sector->setVertexArray(vertices);
	 sector->setNormalArray(normals);
	 sector->addPrimitiveSet(sides);
}

标签:curSegmentSize,float,back,三维,圆弧,push,多段,sides,osg
From: https://www.cnblogs.com/wangxydela/p/18372711

相关文章

  • 【Head3D X 2024 三维RGB - 头部三维数据集-RGB-高精度-三维建模渲染-高清】
    Head3DX2024商用数据集一、数据标注信息(数据量20w+)1)高清原图(短边高于等于2000像素)2)三维头部mesh(obj文件)3)三维虚拟相机内参(npy文件)4)二维头部关键点信息(npy文件5)姿态角(pitch,roll,yaw)检测(基于相机坐标系)三维维建模渲染示例:(Head3D2024三维渲染......
  • manim 中的三维视角
    今天研究了一下manim中的ThreeDAxes和set_camera_orientation这里记录一下视角。manim中的3D坐标系是笛卡尔的三维坐标系,属于右手坐标系,即右手的大拇指,食指,中指垂直,大拇指指向x轴,食指指向Y轴,中指指向z轴。而在初始化3D坐标系,x轴指向屏幕下方,y轴是指向屏幕左......
  • 在线三维CAD中如何实现二维CAD图转三维
    一、前言网页CAD中经常有这样的需求:将二维的CAD图纸通过转换为三维的形状(如将平面二维的图形向上拉伸一定的高度),进而进行三维展示的效果,本文以将平面二维的图形向上拉伸一定的高度为例,实现二维CAD图形转三维图形。二、mxcad和mxcad3dmxcad是一个功能丰富、简易高效的二维CAD开......
  • unity游戏源码和教程:智能分析话语的三维唯美世界
    我不想和任何人说话,大家不要打扰我。这个游戏的源码(含教程文档)我放到了夸克网盘https://pan.quark.cn/s/618fb9459029小区:小区傍晚的雪:小区的晚上:家里:市中心:市街道:郊区:(一)声明小区场景的三维模型来自于UnityStore的unity包:LowPolyJapaneseHousingComplex。很......
  • 三维建模软件:地理信息与遥感领域的智慧构建者
    在地理信息与遥感技术的广阔舞台中,建模软件如同一位卓越的建筑师,以数据为砖瓦,智慧为水泥,构建出一个又一个又一个逼真、动态的虚拟世界。本文将深入探究其技术核心、应用实例、未来趋势,揭示建模软件如何在地理信息与遥感技术中塑造未来。 建模软件的技术核心......
  • 神经网络之卷积篇:详解三维卷积(Convolutions over volumes)
    详解三维卷积从一个例子开始,假如说不仅想检测灰度图像的特征,也想检测RGB彩色图像的特征。彩色图像如果是6×6×3,这里的3指的是三个颜色通道,可以把它想象成三个6×6图像的堆叠。为了检测图像的边缘或者其他的特征,不是把它跟原来的3×3的过滤器做卷积,而是跟一个三维的过滤器,它的维......
  • 三维建模神器:Revit2021怎么下载安装?Revit2021版图文安装教程
    Revit是一款专门为建筑信息模型(BIM)构建的,可以帮助建筑设计师设计、建造和维护质量更好、更高效的建筑,也是现在建筑行业必备的软件之一,现在已经更新了2021的版本,我拿到之后进行了测试,发现还不错,今天我们就来看看Revit2021版本的下载安装方法,详细请看下文图文介绍。软件安装包......
  • 实景三维:解锁地理信息新维度,引领未来城市智慧之钥
    在这个信息爆炸与科技日新月异的时代,地理信息与遥感技术正以前所未有的速度改变我们认知世界的方式。在推动“实景三维平台”这一前沿科技的构建上,它不仅是地理信息的立体呈现,更是智慧城市的基石,打开了通往未来城市规划、管理、决策与服务的新视界。实景三维平台:数字世......
  • 增强现实系列—深入探索ARKit:平面检测、三维模型放置与增强现实交互
    ......
  • 8.9 线段树板子+三分补题+三维的bfs
    nowcoder训练区间线段树板子题,我们只需要把区间每一个点设置成1,然后修改的时候直接改点,然后查区间就行线段树维护最大字段和/01串最大连续1的个数模板题。把白色和黑色看成1/0两个数就行了。#include<bits/stdc++.h>usingnamespacestd;usingi64=longlon......