Rpg游戏地形生成

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值