Rpg游戏地形生成

news2024/11/18 15:50:59

rpg游戏中的地形一般使用高度图的形式来绘制。写了几个随机生成高度图的算法。

最常见的是基于分形算法生成高度图,网上有很多资料,这里不再介绍。

一种生成断层效果高度图的算法

//!生成断层效果的高度图
void TerrainData::FillFaultSurface(float minHeight, float maxHeight , int iterNum,float fSmooth)
{
	const int size = m_vertexNum.x*m_vertexNum.y;
	TerrainVertex* d = m_vertices;
	for (int i=0; i<size; i++) 
	{
		d->y = 0;
		d++;
	}

	int iRandX1, iRandZ1;
	int iRandX2, iRandZ2;
	int iDirX1, iDirZ1;
	int iDirX2, iDirZ2;

	int iHeight;
	float daltaHeight = 256/iterNum;

	for(int it=0; it<iterNum; it++ )
	{
		//插值升高的高度
		iHeight= 256 - daltaHeight*it;

		{
			//选择两个不重合的随机点
			iRandX1= Rand()%m_vertexNum.x;
			iRandZ1= Rand()%m_vertexNum.y;
			do
			{
				iRandX2= Rand()%m_vertexNum.x;
				iRandZ2= Rand()%m_vertexNum.y;
			} while ( iRandX2==iRandX1 && iRandZ2==iRandZ1 );


			//
			iDirX1= iRandX2-iRandX1;
			iDirZ1= iRandZ2-iRandZ1;

			for(int z=0; z<m_vertexNum.y; z++ )
			{
				for(int x=0; x<m_vertexNum.x; x++ )
				{
					//iDirX2, iDirZ2 is a vector from iRandX1, iRandZ1 to the current point (in the loop)
					iDirX2= x-iRandX1;
					iDirZ2= z-iRandZ1;

					//升高一半平面iHeight
					if( ( iDirX2*iDirZ1 - iDirX1*iDirZ2 )>0 )
						m_vertices[( z*m_vertexNum.x )+x].y += ( float )iHeight;
				}
			}

		}

		// 
		GaussBlur2D( &m_vertices->y,sizeof(TerrainVertex)/4,m_vertexNum.x,m_vertexNum.y,fSmooth);
	}

	GetMinMaxAvg(&m_vertices->y,sizeof(TerrainVertex)/4,m_vertexNum.x*m_vertexNum.y,&m_bottomHeight,&m_topHeight,&m_avgHeight);
	RescaleHeight(minHeight, maxHeight);
}

对高度图进行降雨腐蚀的算法

//MaxDropLife   = 30;     // 最大雨滴寿命
//ErodeSpeed    = 0.3f;   // 侵蚀速度
//DepositSpeed  = 0.3f;   // 沉积速度
//ErosionRadius = 3;      // 侵蚀半径
void TerrainData::ErosionSurface(float mount,int MaxDropLife,float ErodeSpeed,float DepositSpeed,int ErosionRadius)
{
	float Inertia;               // 惯性= 0.05
	float SedimentCapacityFactor;// 沙容量= 4
	float MinSedimentCapacity;   // 沙容量= 0.01
	float EvaporateSpeed;        // 蒸发速度 = 0.01
	float Gravity;               // 重力=4
	float Resistance;            // 阻力=0.1

	float InitialWater;          // 初始雨滴水量= 1
	float InitialSpeed;          // 初始雨滴速度= 1

	Inertia = 0.05f;
	SedimentCapacityFactor = 4;
	MinSedimentCapacity = 0.01f;

	if (ErosionRadius==1)  //防止无限腐蚀和沉积 (腐蚀了1单位高度,导致速度增加n单位,容沙量增加m单位,又导致降低>1单位高度,无限循环)
	{	
		SedimentCapacityFactor = 0.05f;
		Gravity = 0.9f;   
	}
	else if (ErosionRadius==2)
	{
		SedimentCapacityFactor = 4;
		Gravity = 4;       
	}
	else if (ErosionRadius==3)
	{
		SedimentCapacityFactor = 4;
		Gravity = 4;       
	}

	EvaporateSpeed = 0.01f;
	MaxDropLife = 30;
	InitialWater = 1;
	InitialSpeed = 1;
	Resistance = 0.1f; //影响不大

	const int mapSize = m_vertexNum.x;
	struct BrushPt
	{
		int    num;
		float* py[49];
		float  weight[49];
	};
	BrushPt*   BrushWeights;
	{
		BrushWeights = new BrushPt[mapSize * mapSize];

		int   indexNum = 0;
		int   indexs[256];
		float weights[256];
		float weightSum = 0;

		const int  num = mapSize * mapSize;
		const float devRadius = 1.0f/ErosionRadius;
		const float radiusSq = ErosionRadius*ErosionRadius;
		for(int i = 0; i < num; i++)
		{
			int centerX = i % mapSize;
			int centerY = i / mapSize;
			weightSum = 0;
			indexNum = 0;
			for(int y = -ErosionRadius; y <= ErosionRadius; y++)
			{
				for(int x = -ErosionRadius; x <= ErosionRadius; x++)
				{
					float dstSq = x * x + y * y;
					if(dstSq < radiusSq)
					{
						int iX = centerX + x;
						int iY = centerY + y;
						if(iX >= 0 && iX < mapSize && iY >= 0 && iY < mapSize)
						{
							float weight = 1 - sqrt(dstSq)*devRadius;
							weightSum += weight;
							weights[indexNum] = weight;
							indexs[indexNum] = y* mapSize + x + i;
							indexNum++;
						}
					}
				}
			}

			BrushPt* pt = &BrushWeights[i];
			pt->num = indexNum;
			for(int j = 0; j < indexNum; j++)
			{
				pt->py[j]     = &m_vertices[indexs[j]].y;
				//权重之和为1
				pt->weight[j] = weights[j] / weightSum;
			}
		}
	}


	const int DropNum = 1+mapSize*mapSize*mount;
	//	迭代步骤:
	//	增加水滴。均匀随机分布
	//	计算地形坡度。确定水滴方向和速度。
	//	计算泥沙容量。受坡度,水速和水量的影响。
	//	侵蚀或沉积。 携沙量>容量则沉积,否则侵蚀。
	//	更新水滴位置下山。
	//	蒸发。

	//const int mapSize_2 = ;
	//快速单水滴独立模拟,没有使用pch流体模拟
	vec3  heightAndGradient;
	vec3  newHeightAndGradient;
	int   dropIndex;//posindex
	vec2  dropPos;
	vec2  dropDir;
	float dropSpeed;
	float dropWater;
	float dropSediment;

	for(int d = 0; d < DropNum; d++)
	{
		dropPos.x = RandRange(0.0f, mapSize - 1.0f);//
		dropPos.y = RandRange(0.0f, mapSize - 1.0f);
		dropDir.x = 0;
		dropDir.y = 0;
		dropSpeed = InitialSpeed;
		dropWater = InitialWater;
		dropSediment = 0;
		//
		for(int life = 0; life < MaxDropLife; life++)
		{
			int x0 = int(dropPos.x);
			int y0 = int(dropPos.y);
			dropIndex = y0 * mapSize + x0;

			//计算水滴高度 梯度(流动方向)
			CalHeightAndGradientLf(dropPos.x, dropPos.y,heightAndGradient);
			dropDir.x = (dropDir.x * Inertia - heightAndGradient.x * (1 - Inertia));
			dropDir.y = (dropDir.y * Inertia - heightAndGradient.z * (1 - Inertia));
			NormalizeFastVec2(dropDir); 
			//移动一个地形格子,与速度无关
			dropPos.x += dropDir.x; 
			dropPos.y += dropDir.y;

			//到达边界 有水土流失
			if(dropPos.x < 0 || dropPos.x >= mapSize - 2 || dropPos.y < 0 || dropPos.y >= mapSize - 2)
				break;
			 //蒸发干净
			if(dropWater<= 0)                               
				break;
			//到达平地,静止(惯性衰减到0),留在原地继续蒸发殆尽影响不大
			if(dropDir.x==0 && dropDir.y==0)             
			{
				if(dropSediment>0.1f)
					int a = 0;
				break;//有水土流失 这里break影响不大
			}
			//新高度
			float newHeight = CalHeightAndGradientLf(dropPos.x, dropPos.y,newHeightAndGradient);//GetHeightLf(dropPos.x, dropPos.y);
			//下降的高度
			float deltaHeight = newHeight - heightAndGradient.y;
			//容沙量 (与坡度 速度 水量相关)
			float sedimentCapacity = -deltaHeight * dropSpeed * dropWater * SedimentCapacityFactor;
			if (sedimentCapacity<MinSedimentCapacity)
				sedimentCapacity = MinSedimentCapacity;

			if(dropSediment > sedimentCapacity //携沙量 > 容沙量 
				|| deltaHeight > 0         // 上山 
				)
			{
				//沉积:
				float toDeposit;
				if(deltaHeight > 0)
				{
					toDeposit = Min(deltaHeight, dropSediment);//上山 fill up填平当前位置 
				}
				else
				{
					toDeposit = (dropSediment - sedimentCapacity) * DepositSpeed;
					//if(toDeposit > -deltaHeight)
					//	toDeposit = -deltaHeight;//沉积高度不超过下降高度->避免出现尖刺
				}
	

				//画刷内部 按权重沉积
				int    ptNum = BrushWeights[dropIndex].num;
				BrushPt* brushWeight   = &BrushWeights[dropIndex];
				for(int b = 0; b < ptNum; ++b)
				{
					float weighedDeposit = toDeposit * brushWeight->weight[b];
					float delta =  weighedDeposit;
					float maxDelta = heightAndGradient.y + weighedDeposit - (*brushWeight->py[b]);
					if(delta> maxDelta)//邻边非常高时不沉积,防止产生尖刺
						delta = maxDelta;
					if(delta>0)
					{
						(*brushWeight->py[b]) += delta;
						dropSediment -= delta;//权重之和不一定为1
					}
				} 
			}
			else
			{
				//腐蚀:腐蚀高度不超过下降高度->避免出现空洞
				float toErode = (sedimentCapacity - dropSediment) * ErodeSpeed;
				if(toErode>-deltaHeight)
					toErode = -deltaHeight;//腐蚀高度不超过下降高度->避免出现空洞

				//画刷内部
				int    ptNum = BrushWeights[dropIndex].num;
				BrushPt* brushWeight   = &BrushWeights[dropIndex];
				for(int b = 0; b < ptNum; ++b)
				{
					float weighedErode = toErode * brushWeight->weight[b];
					float delta =  weighedErode;

					float maxDelta = (*brushWeight->py[b]) - (heightAndGradient.y - weighedErode);
					if(delta> maxDelta)//邻边非常低时不腐蚀,防止产生尖井
						delta = maxDelta;
					if (delta>0)
					{
						(*brushWeight->py[b]) -= delta;
						dropSediment += delta;
					}
				}
			}

			//if(deltaHeight<0)
			{
				dropSpeed = sqrt(dropSpeed * dropSpeed + (-deltaHeight) * Gravity - Resistance);  //重力和阻力做功
			}
			dropWater *= (1 - EvaporateSpeed);
		}
	}
	
	SafeDeleteArray(BrushWeights);
}

梯田化地形  风吹形成山脊

//!风吹沙地形成山脊线  divide点数 首尾在边界
void TerrainData::BlowSurface(const vec3& windDir_,int divide,float weight,float variation)
{
	//方法一 使用Voronoi图
	//1,生成10*10个整齐排列的点,
	//2,每个点随机便移一个位置
	//3,以每个点为中心画逐渐扩大的圆,交线为voronoi的边
	//4,voronoi的每个晶格内使用球挖除地表

	//地表上每个点的挖除深度仅有voronoi中距离最近的点决定 使用格子划分提高查找速度
#define MaxVoronoi 40
	Clamp( divide,1,MaxVoronoi-1);
	static vec2 points[MaxVoronoi][MaxVoronoi];
	float vorinoiCellWidth = m_vertexNum.x/(divide-1);
	//float Up = (vorinoiCellWidth*vorinoiCellWidth)/4;//=200
	float Up = (vorinoiCellWidth*vorinoiCellWidth);//=200
	for (int z=0;z<divide;z++)
	{
		for (int x=0;x<divide;x++)
		{
			points[z][x] = vec2(x*vorinoiCellWidth,z*vorinoiCellWidth);
			points[z][x] += vec2(RandRange(-1.0f,1.0f),RandRange(-1.0f,1.0f))*variation*vorinoiCellWidth;
		}
	}
	//
	const int size = m_vertexNum.x*m_vertexNum.y;
	vec2 vpos;
	TerrainVertex* v = m_vertices;
	for(int z=0; z<m_vertexNum.y; z++ )
	{
		v = m_vertices+z*m_vertexNum.x;
		vpos.y = z;
		for(int x=0; x<m_vertexNum.x; x++ )
		{
			vpos.x = x;
			float distsq = MaxFloat;

			int ox = x/vorinoiCellWidth;
			int oz = z/vorinoiCellWidth;

			for (int cx=ox-2;cx<=ox+2;cx++)
			{
				for (int cz=oz-2;cz<=oz+2;cz++)
				{
					if (  cx>=0&&cx<divide
						&&cz>=0&&cz<divide)
					{
						float t = (points[cz][cx]-vpos).LengthSq();
						if (distsq>t)
						{
							distsq = t; 
						}
					}
				}
			}
			if (distsq>999999)
			{
				distsq = 0;
			}
			if (distsq>Up+10)
			{
				int a = 0;
			}
			//
			v->y -= (Up-distsq)*weight;
			v++;
		}
	}
}

集成到一个流程图编辑器

给腐蚀后的地面加入自动生成的降雨水流湖面,术语好像叫地表径流。

struct WaterPixel
{
	float terrainHeight;
	float waterDepth;
	float totalHeight;
	//float sediment;
	//float flowSpeed;//(for rendering)
	WaterPixel* neighbour[4];
	//float inFlow; // sum of water inflow 
	float outflow[4];
};

int  TerrainData::GenTerrainWaterMap(TextureData& srcTexData,TextureData& dstTexData,
									 const int IterNum/*=100*/,const float RainSpeed/*=0.05f*/,const float StartWater/*=0.01f*/,
									 const float AlphaScale/*=0.1f*/,const float DepthCutoff/*=45*/,
									 const float edgeWidth/* = 10*/,const bool Sharp/*=false*/)
{
	const float FlowDamp = 0.95f;
	const float FlowSpeed = 0.2f;	
	if (srcTexData.GetWidth()!=dstTexData.GetWidth() || srcTexData.GetHeight()!=dstTexData.GetHeight())
	{
		return 0;
	}

	//				//精确求解较复杂  需要多次分段计算 所有的水加起来从最低处单根灌起 然后到第二低开始灌两根 
	//				/*
	//				         ~
	//				         #     ~   
	//						 #     ~ ~
	//						 #     # ~
	//				   ----- # --- # ~-------
	//						 #     # ~
	//						 # ~ ~ # ~ 
	//						 # ~ # # ~
	//						 # # # # #
	//						 # # # # #
	//				*/

	vec2I    waterSize   = m_tileNum; 
	vec2I    waterSize_1 = vec2I(waterSize.x-1,waterSize.y-1);

	WaterPixel* waterData = new WaterPixel[waterSize.x*waterSize.y];
	memset(waterData,0,sizeof(WaterPixel)*waterSize.x*waterSize.y);
	WaterPixel* pixel = waterData;

	vec2 scale;
	scale.x = m_vertexNum.x/float(waterSize.x);
	scale.y = m_vertexNum.y/float(waterSize.y);
	for (int z=0;z<waterSize.y;++z)
	{
		float zf = z*scale.y;
		for (int x=0;x<waterSize.x;++x)
		{
			pixel->terrainHeight = GetHeightLf(x*scale.x,zf);///256 +0.5f;
			pixel->waterDepth = StartWater;
			/*
			      2   
               0     1
			      3   
			*/

			int index0 = z*waterSize.x + x;
			int index;
			{
				index = (z==0)?index0:(index0-waterSize.x);
				pixel->neighbour[2] = &waterData[index];
				index = (z==waterSize_1.y)?(index0):(index0+waterSize.x);
				pixel->neighbour[3] = &waterData[index];
				index = (x==0)?(index0):(index0-1);
				pixel->neighbour[0] = &waterData[index];
				index = (x==waterSize_1.x)?(index0):(index0+1);
				pixel->neighbour[1] = &waterData[index];
			}

			pixel++;
		}
	}

	//
	const float devWaterSize = 1.0f/waterSize.x;
	for(int t=0;t<IterNum;t++)
	{
		WaterPixel* pixel = waterData;
		for (int z=0;z<waterSize.y;++z)
		{
			for (int x=0;x<waterSize.x;++x)
			{
				float totalHeight = pixel->totalHeight;
				float waterHeight = pixel->waterDepth;
				//
				float diffHeight[4] = 
				{
					totalHeight - pixel->neighbour[0]->totalHeight,
					totalHeight - pixel->neighbour[1]->totalHeight,
					totalHeight - pixel->neighbour[2]->totalHeight,
					totalHeight - pixel->neighbour[3]->totalHeight
				};

				//
				pixel->outflow[0] = pixel->outflow[0]*FlowDamp + diffHeight[0] * FlowSpeed ;
				pixel->outflow[1] = pixel->outflow[1]*FlowDamp + diffHeight[1] * FlowSpeed ;
				pixel->outflow[2] = pixel->outflow[2]*FlowDamp + diffHeight[2] * FlowSpeed ;
				pixel->outflow[3] = pixel->outflow[3]*FlowDamp + diffHeight[3] * FlowSpeed ;
				if(pixel->outflow[0]<_EPSILON)pixel->outflow[0]=0;
				if(pixel->outflow[1]<_EPSILON)pixel->outflow[1]=0;
				if(pixel->outflow[2]<_EPSILON)pixel->outflow[2]=0;
				if(pixel->outflow[3]<_EPSILON)pixel->outflow[3]=0;

				//
				float sum = (pixel->outflow[0] + 
							pixel->outflow[1] + 
							pixel->outflow[2] + 
							pixel->outflow[3]);
				float outflowScale = 0;
				if(sum>0)
				{
					outflowScale = waterHeight / sum;
					outflowScale = (outflowScale<1)?outflowScale:1;
				}
				pixel->outflow[0] *= outflowScale;
				pixel->outflow[1] *= outflowScale;
				pixel->outflow[2] *= outflowScale;
				pixel->outflow[3] *= outflowScale;

				pixel++;
			}
		}

		pixel = waterData;
		for (int z=0;z<waterSize.y;++z)
		{
			for (int x=0;x<waterSize.x;++x)
			{
				float waterHeight = pixel->waterDepth;

				float inflow[4] = 
				{
					pixel->neighbour[0]->outflow[1] - pixel->outflow[0], 
					pixel->neighbour[1]->outflow[0] - pixel->outflow[1], 
					pixel->neighbour[2]->outflow[3] - pixel->outflow[2], 
					pixel->neighbour[3]->outflow[2] - pixel->outflow[3]
				};

				//update water
				waterHeight += (
					inflow[0] +
					inflow[1] +
					inflow[2] +
					inflow[3] 
				);

				waterHeight += RainSpeed;			//rain
				waterHeight = (waterHeight>0)?waterHeight:0;

				pixel->waterDepth  = waterHeight;
				pixel->totalHeight = pixel->terrainHeight + waterHeight;
				pixel++;
			}
		}
	}

	//DepthCutoff
	pixel = waterData;
	for (int z=0;z<waterSize.y;++z)
	{
		for (int x=0;x<waterSize.x;++x)
		{
			float waterHeight = pixel->waterDepth;
			waterHeight -= DepthCutoff;		
			//if(waterHeight<0)
			//	waterHeight = 0.0f;
			pixel->waterDepth = waterHeight;
			pixel++;
		}
	}


	{
		int dstSizeX = m_vertexNum.x;
		int dstSizeY = m_vertexNum.y;
		vec2 heightScale;
		heightScale.x = (float(waterSize.x))/dstSizeX;
		heightScale.y = (float(waterSize.y))/dstSizeY;
		TerrainVertex* dstVert = m_vertices;
		int   indexs[4];
		float weights[4];
		float depths[4];
		for (int h=0;h<dstSizeY;h++)
		{
			float zf = h*heightScale.y;
			for (int w=0;w<dstSizeX;w++)
			{
				float xf = w*heightScale.x;
				Lerp_Bilinear(xf,zf,waterSize_1.x,waterSize_1.y,waterSize.x,indexs,weights);

				depths[0] = waterData[indexs[0]].waterDepth;
				depths[1] = waterData[indexs[1]].waterDepth;
				depths[2] = waterData[indexs[2]].waterDepth;
				depths[3] = waterData[indexs[3]].waterDepth;

				float depth = 
					weights[0] * depths[0]
				+ weights[1] * depths[1]
				+ weights[2] * depths[2]
				+ weights[3] * depths[3];

				dstVert->yWater = dstVert->y + depth;
				dstVert ++;
			}
		}
	}

	//==================^_^
	{
		int dstSizeX = dstTexData.GetWidth();
		int dstSizeY = dstTexData.GetWidth();
		vec2 heightScale;
		heightScale.x = (float(waterSize.x))/dstSizeX;
		heightScale.y = (float(waterSize.y))/dstSizeY;
		float color[3];
		color[0] = 48;
		color[1] = 52;
		color[2] = 108;
		int bits = dstTexData.HasAlpha()?4:3;
		unsigned char* srcImageData = srcTexData.GetImageData();
		unsigned char* dstImageData = dstTexData.GetImageData();
		int   indexs[4];
		float weights[4];
		for (int h=0;h<dstSizeY;h++)
		{
			float zf = h*heightScale.y;
			for (int w=0;w<dstSizeX;w++)
			{
				float xf = w*heightScale.x;
				Lerp_Bilinear(xf,zf,waterSize_1.x,waterSize_1.y,waterSize.x,indexs,weights);
				float depth = 
					weights[0] * waterData[indexs[0]].waterDepth
					+ weights[1] * waterData[indexs[1]].waterDepth
					+ weights[2] * waterData[indexs[2]].waterDepth
					+ weights[3] * waterData[indexs[3]].waterDepth;

				if (depth>0)
				{
					float blend = depth;
					blend *= AlphaScale;
					if(blend<0) blend = 0;
					else if(blend>1) blend = 1;
					if(Sharp)
						blend *= blend;
					float blendd = 1-blend;

					//河边描边 河底 都是黄色沙滩   
					dstImageData[0] = 255*blend + srcImageData[0]*blendd;
					dstImageData[1] = srcImageData[1]*blendd;
					dstImageData[2] = srcImageData[2]*blendd;
					dstImageData[3] = srcImageData[3]*blendd;
				}
				else if (depth>-edgeWidth)
				{
					float blend = 1+depth/edgeWidth;
					//blend *= AlphaScale;
					if(blend<0) blend = 0;
					else if(blend>1) blend = 1;
					//if(Sharp)
					//	blend *= blend;
					float blendd = 1-blend;

					//河边描边 河底 都是黄色沙滩   
					dstImageData[0] = srcImageData[0]*blendd;
					dstImageData[1] = srcImageData[1]*blendd;
					dstImageData[2] = srcImageData[2]*blendd;
					dstImageData[3] = 255*blend + srcImageData[3]*blendd;
				}
				else
				{
					dstImageData[0] = srcImageData[0];
					dstImageData[1] = srcImageData[1];
					dstImageData[2] = srcImageData[2];
					dstImageData[3] = srcImageData[3];
				}

				srcImageData += bits;
				dstImageData += bits;
			}
		}
	}

	SafeDeleteArray(waterData);
	return 0;
}

为水面加上shader


#if !GLSLSHADER 
#include "data/shader/ps_common.h" 
#include "data/shader/math.h" 
#endif

#define texWaterNoise baseTexture 
//uniform sampler2D texWaterNoise _TEX0;
uniform sampler2D tex1Refract   _TEX1;
uniform sampler2D tex2Reflect   _TEX2;
uniform sampler2D texGeometry1  _TEX3;
uniform sampler2D texFoam       _TEX4;
uniform sampler2D texNormalMap  _TEX5;
uniform sampler2D texFlowMap    _TEX6;

uniform float     waveTime;
uniform float     amplitude;

uniform float4x4  matViewPrjInverse;

uniform float3    vEyePos ;
//uniform float3    vLightPos;
uniform float3    vLightDir; 


//插值水面 模拟积水变干
uniform vec4 ripplePos[10];
uniform vec4 ripplePower[10];

//uniform float2   worldSize;// = float2(2000, 2000);

//uniform int       typeDrive;

//==================^_^==================^_^==================^_^==================^_^
#if GLSLSHADER
#define inPosW     gl_TexCoord[0]
#define inPosWVP   gl_TexCoord[1]
#define inNormal   gl_TexCoord[2]
#endif

#ifdef ps_water
PSMAIN ps_water(	
#if !GLSLSHADER
				in  float4 inPosW         : TEXCOORD0, 
				in  float4 inPosWVP       : TEXCOORD1,
				in  float3 inNormal       : TEXCOORD2,
				out float4 outColor       : COLOR
#endif
				)
{
	float4 WaterColor = float4(1.0, 1.0, 0.05,1);
	float3 rendScale = float3(1.,1.,1.); //模型单位大小统一后 可去除

	//叠加涟漪2
	float rippleHeight = 0;
	{
		//10个正弦波叠加出涟漪 波长固定
		//ripplePos.xyz=pos    ripplePos.w=相移
		//ripplePower.x=最近   ripplePower.y=最远
		float dist = 0.;
		float height = 0.;
		float amp = 0;
		for(int i=0;i<10;i++)
		{	
			//距离增大 幅度平方衰减 10 ~100
			dist = length(inPosW.xyz - ripplePos[i].xyz);
			if(dist>ripplePower[i].x && dist<ripplePower[i].y)
			{
				amp = min(10/dist,1);
				rippleHeight += amp* sin(3.1415*0.8*dist+ripplePos[i].w);
			}
		}
	}


	float2 texCoord = inPosWVP.xy / inPosWVP.w * 0.5 + float2( 0.5, 0.5 );
#if D3DDRIVER
	texCoord.y = 1 - texCoord.y;
#endif

	float3 dirToLight = - vLightDir;
	float3 dirToEye = normalize( vEyePos - inPosW.xyz );
	//float  disToEye = length( vEyePos - inPosW.xyz );

	//水体通透感:视线深度不同  
	float4 vertexPos;
	UnprojectPosNormal32(tex2D(texGeometry1,texCoord.xy),inPosWVP,matViewPrjInverse,vertexPos);

	float3 waterThrough = vertexPos.xyz - inPosW.xyz;
	float  waterDepth = length(waterThrough*rendScale) + rippleHeight;

	float  disToEye = length( (inPosW.xyz-vEyePos)*rendScale );
	//float  fDistScale = waterDepth/50.;//[0~1]
	float  fDistScale = 1-pow(disToEye,0.1)/4;//[0~1]
	fDistScale = clamp(fDistScale,0,1);//[0~1]

#define FlowMap
	float2 flowDir2 = float2(0.,1.); //必须恒定 否则floawmapoffset随wavetime增大变得夸张并无限大
	float2 flowDir = float2(0.,-1.);

	//inNormal = inNormal*2. - 1.;
	if(abs(inNormal.y)>0.99999)
	{
		//极其水平面 随大流 而不是静止或乱流
		//flowDir.xy = inNormal__.xz*5.;
	}
	else
	{
		//垂直面
		向低势面流动,平滑着色模式 差一点角度 距离差很远
		//flowDir.xy = inNormal__.xz;
		
		flowDir.xy = inNormal.xz;
		flowDir.xy = normalize(flowDir);
		//flowDir = tex2D(texFlowMap, inPosW.xz*0.01);
	}

	float3 vertexNormal;
	//法线 
	{
#ifdef FlowMap
	    float  HalfMaxNOffset = 0.02;
		float  flowOffsetN1 = fmod(waveTime*0.1235,HalfMaxNOffset*2.);
		float  flowOffsetN2 = fmod(waveTime*0.1235+HalfMaxNOffset,HalfMaxNOffset*2.);
		float2 texCoordN1 = inPosW.xz * 0.005*rendScale.xz   - flowDir*flowOffsetN1;
		float2 texCoordN2 = inPosW.xz * 0.00613*rendScale.xz - flowDir*flowOffsetN2;
		float4 colorNormal = lerp(tex2D(texNormalMap, texCoordN1),tex2D(texNormalMap, texCoordN2),abs(flowOffsetN1-HalfMaxNOffset)/HalfMaxNOffset);
#else
		float2 texCoordN1 = inPosW.xz * 0.005*rendScale.xz    + flowDir2*waveTime*0.1235;//+texCoordOffset
		float2 texCoordN2 = inPosW.xz * 0.00613*rendScale.xz  + flowDir2*waveTime*0.233 + float2(20.,0);//错开0.5u,防止周期性重叠
		float4 colorNormal = (tex2D(texNormalMap, texCoordN1)+tex2D(texNormalMap, texCoordN2))/2.;
#endif
		vertexNormal.xyz = Bx2(colorNormal.rgb);
		//TBN1
		//{
		//	vertexNormal.xyz = vertexNormal.rbg; 
		//}
		{//TBN2
			float3 inTangent2 = float3(1.,0.,0.);
			//TBN矩阵变换
			vec3 bitangent  = cross(inNormal.xyz,inTangent2.xyz);
			inTangent2 = cross(bitangent,inNormal.xyz);
			mat3 TBN = mat3(inTangent2.xyz, bitangent, inNormal.xyz);
			TBN = transpose(TBN); //效率?
			vertexNormal.xyz = mul( TBN, vertexNormal).xyz;
		}
		vertexNormal = normalize(vertexNormal);
	}

	//贴于地表
	float2 noiseCoord = inPosW.xz / float2(125.0,125)*rendScale.xz;//波频
	//叠加涟漪
	noiseCoord *= (1. + rippleHeight*0.1);

#ifdef FlowMap
	float  HalfMaxOffset = 0.1;
	float  flowOffset1 = fmod(waveTime*0.8,HalfMaxOffset*2);
	float  flowOffset2 = fmod(waveTime*0.8+HalfMaxOffset,HalfMaxOffset*2);
	float4 noiseColor1 = tex2D(texWaterNoise, noiseCoord - flowDir*flowOffset1);
	float4 noiseColor2 = tex2D(texWaterNoise, noiseCoord - flowDir*flowOffset2);
	float4 noiseColor  = lerp(noiseColor1, noiseColor2, abs(flowOffset1-HalfMaxOffset)/HalfMaxOffset );
	//noiseColor = float4(0.,0.,0.,0.);
#else
	float4 noiseColor = tex2D(texWaterNoise, noiseCoord + flowDir2*waveTime);
#endif
	float2 texCoordOffset = (noiseColor.rg - float2(0.3,0.3))* fDistScale *amplitude; // 幅度
	float2 refrTexCoord = texCoord + texCoordOffset;
	
	//<<
	//折射色方法一: todo根据水的穿透深度调整折射幅度  水的近似垂直深度=vertexPos.y-水平面y
	float4 colorRefract = tex2D(tex1Refract,  refrTexCoord.xy);
	//通透感:水越深折射色越淡  也可以根据深度采样纹理条 这里直接计算
	//float weightRefract = 1.- pow(waterDepth,1.0)/100.;//fDistScale;
	float weightRefract = 1.- pow(waterDepth,0.5)/10.;//fDistScale; //类似fog的指数衰减?
	weightRefract = clamp(weightRefract,0.,1.);
	weightRefract = 0.3 + weightRefract*0.7;

	//折射色方法二:
	//todo屏幕空间折射: 折射光线步进raymarching 深度大于depthbuffer即认为触底 由于水雾的存在 只需步进很短的距离(深水无折射)

	//混合折射色和水色
	outColor = lerp(WaterColor,colorRefract,weightRefract);
	//>>

	//反射色方法二:以水体上边界(可能是曲线)作为对称点采样折射贴图 ,二分查找上边界效率?(水中小岛可能不正确?)

	//屏幕空间反射 效果通常不好? 有可能追不到颜色(颜色未绘制到屏幕上)且有误差带, 180度转动摄像机渲染两遍场景后 在两个屏幕空间结合光追?

	//反射色
	float4 colorReflect = tex2D(tex2Reflect, refrTexCoord.xy);
	//float NdotL = max(dot(dirToEye, vertexNormal), 0.0);
	float  weightReflect = 0.5;//NdotL;
	//float  weightReflect = 1.0 - NdotL;
	//混合反射色和水色
	outColor = lerp(outColor,colorReflect,weightReflect);

	//次表面散射(Sub-Surface Scattering,SSS)

	//高光
	//{
		//phong
		float3 dirRelfect = reflect(vLightDir, vertexNormal);
		float EdotR = dot(dirToEye,dirRelfect);
		float3 specularColor = float3(0.5,0.7,0.7) * pow(max(EdotR, 0.0), 5);
		outColor.rgb += specularColor*0.6;
	//}

	//泡沫
#ifdef FlowMap
    float  HalfMaxFOffset = 0.6;
	float  flowOffsetF1 = fmod(waveTime*5.1235,HalfMaxFOffset*2);
	float  flowOffsetF2 = fmod(waveTime*5.1235+HalfMaxFOffset,HalfMaxFOffset*2);
	float2 texCoordF1 = inPosW.xz * 0.16*rendScale.xz - flowDir*flowOffsetF1;
	float2 texCoordF2 = inPosW.xz * 0.16*rendScale.xz - flowDir*flowOffsetF2;
	float4 colorFoam1 = tex2D(texFoam,  texCoordF1);
	float4 colorFoam2 = tex2D(texFoam,  texCoordF2);
	float4 colorFoam = mix(colorFoam1,colorFoam2,abs(flowOffsetF1-HalfMaxFOffset)/HalfMaxFOffset );
#else
	float4 colorFoam1 = tex2D(texFoam,  inPosW.xz*0.16*rendScale.xz+texCoordOffset.xy*3. + float2(0.,waveTime)*0.1);
	float4 colorFoam2 = tex2D(texFoam,  inPosW.xz*0.26*rendScale.xz+texCoordOffset.xy*3. + float2(5.,waveTime)*0.1);
	float4 colorFoam = mix(colorFoam1,colorFoam2,sin(waveTime*3.23)*0.3+0.7);
#endif

	//边缘浪花 水的近似垂直深度=vertexPos.y-水平面y 浅的地方为边缘
	//waterDepth = abs(inPosW.y - vertexPos.y);
	//float3 foamBlend = float3(1.,1.,1.) - float3(waterDepth,waterDepth,waterDepth)/float3(1.,3.,10.);
	//foamBlend = clamp(foamBlend,float3(0.,0.,0.),float3(1.,1.,1.));
	float3 foamBlend = float3(waterDepth,waterDepth,waterDepth);
	foamBlend = smoothstep(float3(0.,0.,0.),float3(0.4,0.8,1.),foamBlend) - smoothstep(float3(1.0,2.5,4.0),float3(2.,5.,10.),foamBlend);
	//双正弦叠加
	float waveBlendNoise = dot(sin(inPosW.xz*0.4*rendScale.xz),float2(1.,1.));
	//foamHeight = sin(深度+ time)
	float waveBlend = sin(pow(waterDepth,0.5)*9+waveTime*4. + waveBlendNoise )*0.4+0.6;
	float foamLiumi = dot(colorFoam.rgb,foamBlend) * waveBlend ;
	//交互泡沫
	float3 foamBlendRip = float3(rippleHeight,rippleHeight,rippleHeight)*0.2;
	//foamBlendRip = smoothstep(float3(0.,0.,0.),float3(0.4,0.2,0.1),foamBlendRip);
	foamBlendRip = clamp(foamBlendRip,float3(0.,0.,0.),float3(1.,1.,1.));
	foamLiumi += dot(colorFoam.rgb,foamBlendRip);
	//浪尖泡沫
	{
		//float4 colorJaco = lerp(tex2D(texWaterNoise, texCoord1),tex2D(texWaterNoise, texCoord2),abs(flowOffsetN1-HalfMaxNOffset)/HalfMaxNOffset);
		foamBlendRip = noiseColor.bbb - 0.3; //bba也不能增加交错感需要不同的速率
		foamBlendRip = clamp(foamBlendRip,float3(0.,0.,0.),float3(1.,1.,1.));
		foamLiumi += dot(colorFoam.rgb,foamBlendRip);
	}

	outColor.rgb += float3(foamLiumi,foamLiumi,foamLiumi);
	outColor.a = 1.;
}
#endif

为水体加入 flowmap 这里简单使用梯度来代替flowmap

地形的纹理使用四层纹理混合,使用法线贴图、高光贴图强化细节。

地形刷子可以使用自定义形状刷子,等高线刷子等。可以设置刷子遮罩。

#if !GLSLSHADER 
#include "data/shader/ps_common.h" 
#include "data/shader/math.h"
#endif

uniform float   BlendingType;
uniform float2  materialNum; //对任意uniform初始化话导致cg+d3d正常,但hlsl+d3d时只有target生效

#if GLSLSHADER
#define inTangent       gl_SecondaryColor//[1]
#define inDepth         gl_TexCoord[1]
#define inNormal        gl_TexCoord[2]
#define inPosW          gl_TexCoord[3]
//#define gl_FragColor    gl_FragData[0]   //glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
#define outColor_       gl_FragData[0]
#define geometryColor1  gl_FragData[1]
#define geometryColor2  gl_FragData[2]
#endif

#define texTitles baseTexture
uniform sampler2D texBlendMap    _TEX1;
uniform sampler2D texNormalMap   _TEX2;
uniform sampler2D texMaterialMap _TEX3;

uniform float2   worldSize;// = float2(2000, 2000);
/*
   0 4 8  12
   1 5 9  13
   2 6 10 14
   3 7 11 15
*/
vec2 cornerUV(int corner,vec2 uv)
{
	uv = (fract(uv)*0.498+0.001);//[0~1]=>[0~.5]    256
	//uv += vec2(fmod(corner,2.),floor(corner/2.))*0.5;
	uv += vec2(mod(corner,2),(corner/2))*0.5;
	return uv;
}

float noise(in vec2 uv)
{
    return sin(uv.x)+cos(uv.y);
}

float terrain(in vec2 uv)
{
	int octaves = 7;
    float height = 0.;
    float amplitude = 2./3.;
    float freq = .5;
    float n1 = 0.;
    for (int i = 0; i < octaves; i++)
    {
        uv += vec2(amplitude,freq);
        n1 = (noise((uv) * freq)-n1*height);
        height += n1 * amplitude;
        freq *= 2.1-amplitude;
        amplitude *= 1./3.;
        uv = uv.yx-n1/freq;
    }
    return height;
}

vec2 map(vec3 p, int octaves) {
	float d;
	float mID = -1.0;
	float h = terrain(p.xz);
	d = p.y - h;
	return vec2(d, mID);
}

vec3 calcNormal(vec3 p,vec3 inNormal) 
{
	int octaves = 7;
	p*=0.3;
	const vec3 eps = vec3(0.002, 0.0, 0.0);
	return normalize( vec3(map(p+eps.xyy, octaves).x - map(p-eps.xyy, octaves).x,
			       map(p+eps.yxy, octaves).x - map(p-eps.yxy, octaves).x,//2. * eps.x
			       map(p+eps.yyx, octaves).x - map(p-eps.yyx, octaves).x) );
}
float caclAO(vec3 p,vec3 inNormal) 
{
	int octaves = 7;
	p*=0.3;
	const vec3 eps = vec3(0.002, 0.0, 0.0);
	float h = map(p, octaves).x;
	
	float4 dif =  float4( map(p+eps.xyy, octaves).x-h,
		 map(p-eps.xyy, octaves).x-h,
		map(p+eps.yyx, octaves).x-h,
		map(p-eps.yyx, octaves).x-h);
	
	dif *= 300;
	
	dif = clamp(dif,0.,0.3);

	float ao =  dot( dif,float4(1.,1.,1.,1.));

	ao = clamp(ao,0.,0.5);
	return ao;
}

#ifdef ps_multarget_terrain
PSMAIN ps_multarget_terrain(
#if !GLSLSHADER
					   //in  float4 position     : POSITION,  //无法取得,dx11 可以取得SV_POSITION表示像素位置,坐标为视口大小
					   in  float4 inColor        : COLOR0,
					   in  float3 inTangent      : COLOR1,
					   in  float2 inTexCoord     : TEXCOORD0,
					   in  float2 inDepth        : TEXCOORD1,
					   in  float3 inNormal       : TEXCOORD2,
					   in  float3 inPosW         : TEXCOORD3,
					   out float4 outColor_      : COLOR0,
					   out float4 geometryColor1 : COLOR1,
					   out float4 geometryColor2 : COLOR2
#endif
					   )
{
	float2 inTexCoord_ = mul(matTexture,float4(inTexCoord.xy,1,1)).xy;

	//Shader Model 3.0 不支持纹理数组 , 0~4号纹理拼在tile0中
	//防止垂直的部分纹理拉伸
	float3 normalSq    = inNormal.xyz*inNormal.xyz;
	float3 inPosWWrap  = inPosW.xyz*16.0/worldSize.x; //16重

	//默认2层 平层+陡峭层  或按高度分三层 都可以在编辑器搞定,这里只需混合
	outColor_.rgb = vec3(0.,0.,0.); 
	
	float4 blendColor    = tex2D(texBlendMap,inTexCoord_);
                           
	float3 outNormal     = inNormal.xyz;
	float3 normalColor   = float3(0.,0.,0.); 
	float4 materialColor = float4(0.,0.,0.,0.); 

	//叠加4层 	 0~16号纹理拼在tile0中
	float4 color[3];
	float  weightSum = 0.;
	for(int id=0;id<4;id++)
	{
		float weight = blendColor.r;
		if(weight>0.)
		{
			//if(dot(inNormal.xyz,vec3(0.,1.,0.))<0.999)
			//{
			//	//垂直面
			//	vec2 coord1 = cornerUV(id,inPosWWrap.xy);
			//	vec2 coord2 = cornerUV(id,inPosWWrap.yz);
			//	vec2 coord3 = cornerUV(id,inPosWWrap.zx);
			//	color[0] = tex2D(texTitles,coord1)*normalSq.z;
			//	color[1] = tex2D(texTitles,coord2)*normalSq.x;
			//	color[2] = tex2D(texTitles,coord3)*normalSq.y;
			//	outColor_.rgb += (color[0]+color[1]+color[2]).rgb * weight;
			//	color[0] = tex2D(texNormalMap,coord1)*normalSq.z;
			//	color[1] = tex2D(texNormalMap,coord2)*normalSq.x;
			//	color[2] = tex2D(texNormalMap,coord3)*normalSq.y;
			//	normalColor.rgb += (color[0]+color[1]+color[2]).rgb * weight;
			//	materialColor.rgba +=  tex2D(texMaterialMap ,coord3).rgba * weight;
			//}
			//else
			{
				vec2 coord = cornerUV(id,inPosWWrap.xz);
				//color[0] = tex2D(texTitles,cornerUV(id,inPosWWrap.xy))*normalSq.z;
				//color[1] = tex2D(texTitles,cornerUV(id,inPosWWrap.yz))*normalSq.x;
				//color[2] = tex2D(texTitles,cornerUV(id,inPosWWrap.zx))*normalSq.y;
				//outColor_.rgb += (color[0]+color[1]+color[2]).rgb * weight;
				outColor_.rgb      += tex2D(texTitles      ,coord).rgb  * weight;
				normalColor.rgb    += tex2D(texNormalMap   ,coord).rgb  * weight;
				materialColor.rgba += tex2D(texMaterialMap ,coord).rgba * weight;
				
			}
			weightSum += weight;
		}
		blendColor.rgba = blendColor.gbar;
	}

	//此处不需要细节纹理,细节纹理要采用不同的wrap比例才有效果

	if(weightSum!=0) //weightSum可能<1
	{
		normalColor   /= weightSum;
		materialColor /= weightSum;
	}
	//如果不做TBN矩阵变换,则只有模型面正好面向z正时法线显示正确
	normalColor = normalize(normalColor * 2.0 - 1.0);  

	vec3 tangent   = inTangent.xyz;//vec3(1,0,0);
	vec3 bitangent = cross(tangent,inNormal.xyz);
	//mat3 TBN = mat3(1,0,0, 0,0,1, 0,1,0);
	mat3 TBN = mat3(tangent, bitangent, inNormal.xyz);
	TBN = transpose(TBN); 
	outNormal.xyz = mul( TBN, normalColor).xyz;

	//{
	//	//模拟层页岩纹理  xy + zy 采样两次纹理图后混合 (只靠法线贴图不行,需要ao光照图)
	//	outNormal.y += pow((sin(inPosW.y*10)-1)*0.2,0.5);
	//	outNormal = calcNormal(inPosW.xyz,outNormal);
	outColor_.rgb *= (1.-caclAO(inPosW.xyz,outNormal));
	//}
	outNormal.xyz = normalize( outNormal.xyz);//必须 否则噪点

	//{
	//	//模拟积雪纹理  下雪的方向 阳光融化方向 凹凸性 https://www.shadertoy.com/view/MlGBD1  https://www.shadertoy.com/view/lsKGW3  
	//	//雪花纹理https://www.shadertoy.com/view/Xsd3zf
	//	//float snowHeight = step(0.7,outNormal.y);
	//	float snowHeight = smoothstep(0.7,1.0,outNormal.y);
	//	outColor_.rgb += snowHeight;
	//}

	outColor_.a = 1.;

	//if(BlendingType==0)//Filter
	{
		//clip之后的顶点坐标(x, y, z, w),在OpenGL顶点经过viewport变换写入深缓的z是(z/w + 1) / 2,D3D上是z/w
		float depthvalue = inDepth.x/inDepth.y; //[0, 1]

		//不允许 target1、2单独设置混合模式

		//ab: am di
		//rg: sp lv
		float2 materialNum_;
		materialNum_.x  = EncodeFloat2Color1_RGBA32(materialColor.ab); //am di
		materialNum_.y  = EncodeFloat2Color1_RGBA32(materialColor.rg); //sp lv

		//gbuffer parse
		geometryColor1 = float4(depthvalue,materialNum_.x,materialNum_.y,1);
		geometryColor2 = float4(EncodeNormal(outNormal),0,1);

		//简单处理  alpha为0 不影响深度  alpha不为0混合影响深度  todo 最后单独绘制一般?
		geometryColor1.a = outColor_.a;
		geometryColor2.a = outColor_.a;
	}  
}
#endif

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

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

相关文章

12.4作业

#include <iostream>using namespace std;class Sofa { private:string sit;int *nub; public:Sofa(){cout << "Sofa::无参构造函数" << endl;}Sofa(string sit,int nub):sit(sit),nub(new int(nub)){cout << "Sofa::有参构造函数"…

【刷题日志】牛客 HJ73 计算日期到天数转换

计算日期到天数转换 阅读题目解题方案 及 解题思路方法一 . 手撕日期类方法二 . 分别直接算出平年和闰年每个月的时间 并对应下标存入数组中&#xff0c;判断该年份为平年还是闰年&#xff0c;再 for循环依次求和优化&#xff1a;也不用分别算出平年和闰年的每个月的时间&#…

IntelliJ IDEA设置中文界面

1.下载中文插件 2. 点击重启IDE 3.问题就解决啦&#xff01;

目标检测中的损失函数:IOU_Loss、GIOU_Loss、DIOU_Loss和CIOU_Loss

文章目录 前言1.IOU_Loss&#xff08;Intersection over Union Loss&#xff09;2.GIOU_Loss&#xff08;Generalized Intersection over Union Loss&#xff09;3.DIOU_Loss&#xff08;Distance Intersection over Union Loss&#xff09;4.CIOU_Loss&#xff08;Complete In…

2023年5月电子学会青少年软件编程 Python编程等级考试一级真题解析(选择题)

2023年5月Python编程等级考试一级真题解析 选择题(共25题,每题2分,共50分) 1、可以对Python代码进行多行注释的是 A、# B、“ ” C、‘’‘ ’‘’ D、‘ ’ 答案:C 考点分析:考查python中注释,python中注释分为单行注释和多行注释,单行注释使用井号(#),多…

表达式二叉树的中序遍历:2017年408算法题

算法思想 表达式二叉树的中序遍历即中缀表达式除了根节点和叶结点&#xff0c;遍历到其他结点时在遍历其左子树前加上左括号&#xff0c;在遍历完右子树后加上右括号 算法实现 //中序遍历&#xff0c;deep从1开始&#xff0c;即根节点的深度为1 void midOrder(BTree T,int …

十五、机器学习进阶知识:K-Means聚类算法

文章目录 1、聚类概述2、K-Means聚类算法原理3、K-Means聚类实现3.1 基于SKlearn实现K-Means聚类3.2 自编写方式实现K-Means聚类 4、算法不足与解决思路4.1 存在的问题4.2 常见K值确定方法4.3 算法评估优化思路 1、聚类概述 聚类&#xff08;Clustering&#xff09;是指将不同…

鸿宇多用户商城user.php RCE漏洞复现

0x01 产品简介 鸿宇多用户商城是一款支持各行业的多商家入驻型电商平台系统,商家版APP,微信商城,小程序及各种主流营销模块应有尽有,是一个功能强大的电子商务平台,旨在为企业和个人提供全面的在线购物解决方案。 0x02 漏洞概述 鸿宇多用户商城 user.php 存在任意命令执行漏洞…

jira创建用例,与任务关联

项目用的jira&#xff0c;但之前的用例放在禅道上&#xff0c;或者归档于svn&#xff0c;都不是很好用&#xff0c;所以研究了下jira的用法 1、下载插件&#xff1a; synapseRT - Test management and QA in JIRA 完成后在tab会多出一个test 2、常用的功能 1、建立用例&#…

TCP三次握手与四次挥手:推荐学习资料、过程详解、面试相关题与回答模板(为什么不是两次握手等精讲)

推荐资料&#xff08;建议按照顺序先都看完&#xff0c;再看本篇文章&#xff09; https://www.bilibili.com/video/BV18h41187Ep/ https://www.bilibili.com/video/BV1at4y1Q77b/ https://bbs.huaweicloud.com/blogs/277728 https://blog.csdn.net/dreamispossible/article/d…

paddleocr文本检测改进变迁

数据增强&#xff1a; BDA(Base Data Augmentation)&#xff1a;色调变换&#xff0c;透明度变换&#xff0c;旋转&#xff0c;背景模糊&#xff0c;饱和度变换。 图像变换类&#xff1a;AutoAugment&#xff0c;RandAugment 图像裁剪类&#xff1a;CutOut、RandErasing、Hi…

【UE5】使用场系统炸毁一堵墙

效果 步骤 1. 新建一个空白项目 2. 新建一个Basic关卡&#xff0c;然后添加一个第三人称游戏和初学者内容包到内容浏览器 3. 在场景中添加一堵墙 4. 选项模式选择“破裂” 点击新建 新建一个文件夹用于存储几何体集 点击“统一” 最小和最大Voronoi点数都设置为100 点击“破…

【java设计模式】——代理设计模式,两种举例说明

代理设计模式 1.介绍 Spring 框架中AOP底层使用动态代理设计模式。通过学习动态代理设计模式可以很好的理解Spring框架AOP底层 代理模式&#xff08;Proxy&#xff09;是GoF23种设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象&#xff0c;而是通过调用代理&am…

Web APIs—介绍、获取DOM对象、操作元素内容、综合案例—年会抽奖案例、操作元素属性、间歇函数、综合案例—轮播图定时器版

版本说明 当前版本号[20231204]。 版本修改说明20231204初版 目录 文章目录 版本说明目录复习变量声明 Web APIs - 第1天笔记介绍概念DOM 树DOM 节点document 获取DOM对象案例— 控制台依次输出3个li的DOM对象 操作元素内容综合案例——年会抽奖案例操作元素属性常用属性修改…

天池XGBoost,重写柱状图代码

天池XGBoost 地址 重写柱状图代码&#xff1a;我没考虑复杂度&#xff0c;只考虑直观理解 原文统计地点是否降雨来画柱状图实在是太麻烦了&#xff0c;我重写了一下。最麻烦的就是数据处理。我的思路是&#xff1a; 首先取下雨的全部数据data[data[RainTomorrow] Yes] 然后…

全网最牛最“刑”的Fiddler移动端抓包

本篇文章&#xff0c;博主想使用通俗易懂的话语&#xff0c;让大家明白以下内容&#xff1a; 什么是抓包哪些场景需要用到抓包Fiddler抓包的原理怎样使用Fiddler进行移动端抓包 抓包 包 (Packet) 是TCP/IP协议通信传输中的数据单位&#xff0c;一般也称“数据包”。 我们平常…

二维码智慧门牌管理系统升级:轻松解决重新制牌问题

文章目录 前言一、更便捷的申请方式二、系统优势 前言 随着科技的快速发展&#xff0c;智能化管理已经成为我们日常生活的一部分。最近&#xff0c;为了满足人们对门牌类型更换、门牌丢失等需要重新制牌的需求&#xff0c;二维码智慧门牌管理系统升级了解决方案&#xff0c;为…

Python如何从文件中读取数据

从文件中读取数据 1. 读取整个文件 要读取文件&#xff0c;首先来创建一个文件&#xff1a; 然后打开并读取这个文件&#xff0c;再将其内容显示到屏幕上&#xff1a; file_reader.py with open(pi_digits.txt) as file_object:contents file_object.read()print(contents)…

创新、升级丨数据手套FOHEART Pro开启手势识别新篇章!

在人机交互领域&#xff0c;我们始终追求更加自然、逼真的体验。正如现实生活中&#xff0c;我们习惯于通过语言和表情来传达思想和情感&#xff0c;然而&#xff0c;在虚拟世界中&#xff0c;人机交互需要以更加直观、生动的方式进行操作、控制和交互。 为了更好地满足市场的…

Elasticsearch:评估 RAG - 指标之旅

作者&#xff1a;Quentin Herreros&#xff0c;Thomas Veasey&#xff0c;Thanos Papaoikonomou 2020年&#xff0c;Meta发表了一篇题为 “知识密集型NLP任务的检索增强生成” 的论文。 本文介绍了一种通过利用外部数据库将语言模型 (LLM) 知识扩展到初始训练数据之外的方法。 …