笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

海水仿真渲染一直是比较困难的事情,虽然市面上有了各种海水的渲染算法,但是真正做到仿真渲染的少之又少。大多停留在试验阶段,达到了仿真的要求,但是硬件配置要求高,不利于推广。本篇博客给读者介绍的是关于海水的实施渲染以及通过算法实现船只航行轨迹效果,真实的达到了海水的渲染,海水的网格采用了多个面片网格拼接的方式,网格采用的是LOD处理的,这样就优化了效率。同时将海水的绘制以及算法实现放到C++中进行,这样对于复杂算法的实现效率明显提升。先给读者看几幅效果图:

船在海水中航行的轨迹效果图,轨迹是实时绘制的。再来一副海水到岸边产生的泡沫效果图:

除了岸边的效果图外,船在水中周围也会产生泡沫效果。最后一副效果图如下:

海水的反射折射效果。下面开始给读者介绍实现该海水的原理以及核心代码,最后把整个工程奉献给读者。

第一步:海水网格的实现,海水网格采用的是面片拼接的方式,并且面片采用了LOD运算,其在Unity中的效果如下所示:

从里向外,面片的数量逐步减少,它是根据摄像机的远近处理的,对应的核心代码如下所示:

	//update the meshes with the final calculated mesh data
	void updateTiles(int a, int b) {

		if(skipLods) {
			lodSkip++;
			if(lodSkip >= lodSkipFrames+1) lodSkip=0;
		}

		for (int L0D=a; L0D<b; L0D++) {
			//if(L0D>
			//this will skip one update of the tiles higher then Lod0
			if(L0D>0 && lodSkip==0 && !ticked && skipLods) { break; }
			//this will skip one update of the LOD0 tiles because they got updated earlier when they should.
			if(ticked2 && L0D==0) { ticked2=false; continue; }

			#if !NATIVE
				int den = MyIntPow (2, L0D);
				int idx = 0;

				for (int y=0; y<g_height; y+=den) {
					for (int x=0; x<g_width; x+=den) {
						int idx2 = g_width * y + x;
						verticesLOD[L0D] [idx] = vertices [idx2];
						//lower the far lods to eliminate gaps in the horizon when having big waves
						if(L0D>0) {
							if(farLodOffset!=0) {
								verticesLOD[L0D] [idx].y += flodoffset[L0D] * flodFact;
							}
						}
						tangentsLOD[L0D] [idx] = tangents [idx2];
						normalsLOD[L0D] [idx++] = normals [idx2];
					}
				}
			#else
				uocean._updateTilesA(verticesLOD[L0D], vertices, tangentsLOD[L0D], tangents, normalsLOD[L0D], normals, L0D, farLodOffset, flodoffset, flodFact);
			#endif

			btiles_LOD[L0D].vertices = verticesLOD[L0D];
			btiles_LOD[L0D].normals = normalsLOD[L0D];
			btiles_LOD[L0D].tangents = tangentsLOD[L0D];
		}

		if(ticked) ticked = false;
	}

	void GenerateTiles() {

		int chDist, nmaxLod=0; // Chebychev distance

		for (int y=0; y<tiles; y++) {
			for (int x=0; x<tiles; x++) {
				chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x));
				chDist = chDist > 0 ? chDist - 1 : 0;
				if(nmaxLod<chDist) nmaxLod = chDist;
			}
		}
		max_LOD = nmaxLod+1;

		flodoffset = new float[max_LOD+1];
		float ffact = farLodOffset/max_LOD;
		for(int i=0; i<max_LOD+1; i++) {
			flodoffset[i] = i*ffact;
		}

		btiles_LOD = new List<Mesh>();
		tiles_LOD = new List<List<Mesh>>();

		for (int L0D=0; L0D<max_LOD; L0D++) {
			btiles_LOD.Add(new Mesh());
			tiles_LOD.Add (new List<Mesh>());
		}

		GameObject tile;

		int ntl = LayerMask.NameToLayer ("Water");

		for (int y=0; y<tiles; y++) {
			for (int x=0; x<tiles; x++) {
				chDist = System.Math.Max (System.Math.Abs (tiles / 2 - y), System.Math.Abs (tiles / 2 - x));
				chDist = chDist > 0 ? chDist - 1 : 0;
				if(nmaxLod<chDist) nmaxLod = chDist;
				float cy = y - Mathf.Floor(tiles * 0.5f);
				float cx = x - Mathf.Floor(tiles * 0.5f);
				tile = new GameObject ("Lod_"+chDist.ToString()+":"+y.ToString()+"x"+x.ToString());

                Vector3 pos=tile.transform.position;
				pos.x = cx * size.x;
				pos.y = transform.position.y;
				pos.z = cy * size.z;

				tile.transform.position=pos;
				tile.AddComponent <MeshFilter>();
				tile.AddComponent <MeshRenderer>();
                Renderer renderer = tile.GetComponent<Renderer>();

				tile.GetComponent<MeshFilter>().mesh = btiles_LOD[chDist];
				//tile.isStatic = true;

				//shader/material lod (needs improvement)
				if(useShaderLods && numberLods>1) {
					if(numberLods==2) {
						if(chDist <= sTilesLod) { if(material) renderer.material = material; }
						if(chDist > sTilesLod) { if(material1) renderer.material = material1; }
					}else if(numberLods==3){
						if(chDist <= sTilesLod ) { if(material) renderer.material = material; }
						if(chDist == sTilesLod+1) { if(material1) renderer.material = material1; }
						if(chDist > sTilesLod+1) { if(material2) renderer.material = material2; }
					}
				} else {
					renderer.material = material;
				}

                //Make child of this object, so we don't clutter up the
                //scene hierarchy more than necessary.
                tile.transform.parent = transform;

				//Also we don't want these to be drawn while doing refraction/reflection passes,
				//so we'll add the to the water layer for easy filtering.
				tile.layer = ntl;

				tiles_LOD[chDist].Add( tile.GetComponent<MeshFilter>().mesh);
			}
		}

		//enable/disable the fixed disc
		initDisc();
	}

在Unity中生成的效果如下所示:

第二步,海水的渲染Shader,根据不同的LOD等级实行不同的Shader渲染,举个例子,LOD等级2的Shader代码如下所示:

Shader "Mobile/OceanL2" {
	Properties {
	    _SurfaceColor ("SurfaceColor", Color) = (1,1,1,1)
	    _WaterColor ("WaterColor", Color) = (1,1,1,1)

		_Specularity ("Specularity", Range(0.01,1)) = 0.3
		_SpecPower("Specularity Power", Range(0,1)) = 1

		[HideInInspector] _SunColor ("SunColor", Color) = (1,1,0.901,1)

		_Bump ("Bump (RGB)", 2D) = "bump" {}
		_Size ("UVSize", Float) = 0.015625//this is the best value (1/64) to have the same uv scales of normal and foam maps on all ocean sizes
		[HideInInspector] _SunDir ("SunDir", Vector) = (0.3, -0.6, -1, 0)

		_FakeUnderwaterColor ("Water Color LOD1", Color) = (0.196, 0.262, 0.196, 1)
		_DistanceCancellation ("Distance Cancellation", Float) = 2000
	}

//water bump
     SubShader {
        Tags { "RenderType" = "Opaque" "Queue"="Geometry"}
        LOD 2
    	Pass {
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			//#pragma multi_compile_fog
			#pragma multi_compile FOGON FOGOFF
			#pragma multi_compile DCON	DCOFF

			#pragma target 2.0
			#include "UnityCG.cginc"

			struct v2f {
    			float4 pos : SV_POSITION;
				half3 floatVec : TEXCOORD0;
    			float2  bumpTexCoord : TEXCOORD1;
    			//half3  viewDir : TEXCOORD2;
    			half3  lightDir : TEXCOORD2;
				half2 buv : TEXCOORD3;
				half3 normViewDir : TEXCOORD4;
				//UNITY_FOG_COORDS(7)
				#ifdef FOGON
				half dist : TEXCOORD5;
				#ifdef DCON
				half distCancellation : TEXCOORD6;
				#endif
				#endif
			};

			half _Size;
			half4 _SunDir;
			half4 _FakeUnderwaterColor;
			#ifdef FOGON
 			uniform half4 unity_FogStart;
			uniform half4 unity_FogEnd;
			uniform half4 unity_FogDensity;
			#ifdef DCON
			half _DistanceCancellation;
			#endif
			#endif

			v2f vert (appdata_tan v) {
    			v2f o;
				UNITY_INITIALIZE_OUTPUT(v2f, o);

    			o.bumpTexCoord.xy = v.vertex.xz*_Size;///float2(_Size.x, _Size.z)*5;
    			o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

    			half3 objSpaceViewDir = ObjSpaceViewDir(v.vertex);
    			half3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) );
				half3x3 rotation = half3x3( v.tangent.xyz, binormal, v.normal );

    			half3 viewDir = mul(rotation, objSpaceViewDir);
    			o.lightDir = mul(rotation, half3(_SunDir.xyz));

				o.buv = float2(o.bumpTexCoord.x + _CosTime.x * 0.2, o.bumpTexCoord.y + _SinTime.x * 0.3);

				o.normViewDir = normalize(viewDir);

				o.floatVec = normalize(o.normViewDir - normalize(o.lightDir));

				#ifdef FOGON
				//manual fog
				half fogDif = 1.0/(unity_FogEnd.x - unity_FogStart.x);
				o.dist = (unity_FogEnd.x - length(o.pos.xyz)) * fogDif;
				#ifdef DCON
				o.distCancellation = (unity_FogEnd.x - _DistanceCancellation) * fogDif;
				#endif
                #endif

				//autofog
				//UNITY_TRANSFER_FOG(o, o.pos);

    			return o;
			}

			sampler2D _Bump;
			half4 _WaterColor;
			half4 _SurfaceColor;
			half _Specularity;
			half _SpecPower;
            half4 _SunColor;

			half4 frag (v2f i) : COLOR {

				#ifdef FOGON
				#ifdef DCON
				if(i.dist>i.distCancellation){
				#endif
				#endif
					half3 tangentNormal0 = (tex2D(_Bump, i.buv.xy) * 2.0) -1;
					half3 tangentNormal = normalize(tangentNormal0);

					half4 result = half4(0, 0, 0, 1);

					//half fresnelLookup = dot(tangentNormal,i. normViewDir);
					//float bias = 0.06;
					//float power = 4.0;
					//half fresnelTerm = 0.06 + (1.0-0.06)*pow(1.0 - fresnelLookup, 4.0);

					half fresnelTerm = 1.0 - saturate(dot (i.normViewDir, tangentNormal0));

					half specular = pow(max(dot(i.floatVec,  tangentNormal) , 0.0), 250.0 * _Specularity ) * _SpecPower;

					result.rgb = lerp(_WaterColor*_FakeUnderwaterColor, _SunColor.rgb*_SurfaceColor*0.85, fresnelTerm*0.6)  + specular*_SunColor.rgb;

					//fog
					//UNITY_APPLY_FOG(i.fogCoord, result); 

					#ifdef FOGON
					//manual fog (linear) (reduces instructions on d3d9)
					float ff = saturate(i.dist);
					result.rgb = lerp(unity_FogColor.rgb, result.rgb, ff);
					#endif

    				return result;
				#ifdef FOGON
				#ifdef DCON
				}else{
					return unity_FogColor;
				}
				#endif
				#endif
			}
			ENDCG

		}
    }

}

渲染的效果图如下所示:


远近海水很有层次感效果。

第三步:船只在海水中航行的轨迹效果以及随着海水起伏效果实现代码

using UnityEngine;
using System.Collections.Generic;

public class Boyancy : MonoBehaviour {

	private Ocean ocean;
	public int renderQueue;

	public bool useFixedUpdate = false;
	public bool moreAccurate = false;
	public float magnitude = 2f;
	public float ypos = 0.0f;
	private List<Vector3> blobs;
	private List<float[]> prevBoya;

	//private bool engine = false;
	private List<float> sinkForces;

	public float CenterOfMassOffset = -1f;
	public float dampCoeff = .1f;

	//buoyancy slices. (Cannot be smaller then 2)
	//Raise these numbers if you want more accurate simulation. However it will add overhead. So keep it as small as possible.
	public int SlicesX = 2;
	public int SlicesZ = 2;

	public int interpolation = 3;
	private int intplt;
	public bool ChoppynessAffectsPosition = false;
	public float ChoppynessFactor = 0.2f;

	public bool WindAffectsPosition = false;
	public float WindFactor = 0.1f;

	public bool xAngleAddsSliding = false;
	public float slideFactor = 0.1f;

	public bool cvisible, wvisible, svisible;
	public Renderer _renderer ;

	public bool sink = false;
	public float sinkForce = 3;

	private float iF;
	private bool interpolate = false;

	private Rigidbody rrigidbody;
	private int tick, tack;
	private Vector3 wpos, cpos;
	private bool useGravity;

	private float accel;
	private int prevAngleX, currAngleX;

	private float bbboyancy;
	private float prevBuoyancy;

    void Start () {

		if(!_renderer) {
			_renderer = GetComponent<Renderer>();
			if(!_renderer) {
				_renderer = GetComponentInChildren<Renderer>();
			}
		}

		if(_renderer && renderQueue>0) _renderer.material.renderQueue = renderQueue;

		if(!_renderer) {
			if(cvisible) { Debug.Log("Renderer to check visibility not assigned."); cvisible = false; }
			if(wvisible) { Debug.Log("Renderer to check visibility not assigned."); wvisible = false; }
			if(svisible) { Debug.Log("Renderer to check visibility not assigned."); svisible = false; }
		}

		if(dampCoeff<0) dampCoeff = Mathf.Abs(dampCoeff);

		rrigidbody =  GetComponent<Rigidbody>();

		useGravity = rrigidbody.useGravity;

		if(interpolation>0) {
			interpolate = true;
			iF = 1/(float)interpolation;
			intplt = interpolation;
		}

		if(SlicesX<2) SlicesX=2;
		if(SlicesZ<2) SlicesZ=2;

        ocean = Ocean.Singleton;

		rrigidbody.centerOfMass = new Vector3 (0.0f, CenterOfMassOffset, 0.0f);

		Vector3 bounds = GetComponent<BoxCollider> ().size;

		float length = bounds.z;
		float width = bounds.x;

		blobs = new List<Vector3> ();
		prevBoya = new List<float[]>();

		int i = 0;
		float xstep = 1.0f / ((float)SlicesX - 1f);
		float ystep = 1.0f / ((float)SlicesZ - 1f);

		sinkForces = new List<float>();

		float totalSink = 0;

		for (int x=0; x<SlicesX; x++) {
			for (int y=0; y<SlicesX; y++) {
				blobs.Add (new Vector3 ((-0.5f + x * xstep) * width, 0.0f, (-0.5f + y * ystep) * length) + Vector3.up * ypos);

				if(interpolate) { prevBoya.Add(new float[interpolation]); }

				float force =  Random.Range(0f,1f);
				force = force * force;
				totalSink += force;
				sinkForces.Add(force);
				i++;
			}
		}

		// normalize the sink forces
		for (int j=0; j< sinkForces.Count; j++)	{
			sinkForces[j] = sinkForces[j] / totalSink * sinkForce;
		}

	}

	void Update() {
		if(!useFixedUpdate) update();
    }

	void FixedUpdate() {
		if(useFixedUpdate) update();
    }

	bool visible, lastvisible;
	int lastFrame=-15;

	void update() {

		if (ocean != null) {

			visible = _renderer.isVisible;

			//put object on the correct height of the sea surface when it has visibilty checks on and it became visible again.
			if(visible != lastvisible) {
				if(visible && !lastvisible) {
					if(Time.frameCount-lastFrame>15) {
						float off = ocean.GetChoppyAtLocation(transform.position.x, transform.position.z);
						float y = ocean.GetWaterHeightAtLocation2 (transform.position.x-off, transform.position.z);
						transform.position = new Vector3(transform.position.x, y, transform.position.z);
						lastFrame = Time.frameCount;
					}
				}
				lastvisible = visible;
			}

			//prevent use of gravity when buoyancy is disabled
			if(cvisible) {
				if(useGravity) {
					if(!visible) {
							rrigidbody.useGravity=false;
							if(wvisible && svisible) return;
					} else {
							rrigidbody.useGravity = true;
						}
				}else {
					if(!visible) { if(wvisible && svisible) return;}
				}
			}

			float coef = dampCoeff;
			int index = 0, k=0;

			int ran = (int)Random.Range(0, blobs.Count-1);

			for(int j = 0; j<blobs.Count; j++) {

				wpos = transform.TransformPoint (blobs[j]);
				//get a random blob to apply a force with the choppy waves
				if(ChoppynessAffectsPosition) { if(j == ran)  cpos = wpos; }

				if(!cvisible || visible) {
					float buyancy = magnitude * (wpos.y);

					if (ocean.enabled) {
						if(ocean.canCheckBuoyancyNow[0]==1) {
							float off = 0;
								if(ocean.choppy_scale>0) off = ocean.GetChoppyAtLocation(wpos.x, wpos.z);
							if(moreAccurate) {
								buyancy = magnitude * (wpos.y - ocean.GetWaterHeightAtLocation2 (wpos.x-off, wpos.z));
							}else {
								buyancy = magnitude * (wpos.y - ocean.GetWaterHeightAtLocation (wpos.x-off, wpos.z));
								buyancy = Lerp(prevBuoyancy, buyancy, 0.5f);
								prevBuoyancy = buyancy;
							}
							bbboyancy = buyancy;
						} else {
							buyancy = bbboyancy;
						}
					}

					if (sink) { buyancy = System.Math.Max(buyancy, -3) + sinkForces[index++]; }

					float damp = rrigidbody.GetPointVelocity (wpos).y;

					float bbuyancy = buyancy;

					//interpolate last (int interpolation) frames to smooth out the jerkiness
					//interpolation will be used only if the renderer is visible
					if(interpolate) {
						if(visible) {
							prevBoya[k][tick] = buyancy;
							bbuyancy=0;
							for(int i=0; i<intplt; i++) { bbuyancy += prevBoya[k][i]; }
							bbuyancy *= iF;
						}
					}
					rrigidbody.AddForceAtPosition (-Vector3.up * (bbuyancy + coef * damp), wpos);
					k++;
				}
			}

			if(interpolate) { tick++; if(tick==intplt) tick=0; }

			tack++; if (tack == (int)Random.Range(2, 9) ) tack=0;
			if(tack>9) tack =1;

			//if the boat has high speed do not influence it (choppyness and wind)
			//if it has lower then fact then influence it depending on the speed .
			float fact = rrigidbody.velocity.magnitude * 0.02f;

			//this code is quick and dirty
			if(fact<1) {
				float fact2 = 1-fact;
				//if the object gets its position affected by the force of the choppy waves. Useful for smaller objects).
				if(ChoppynessAffectsPosition) {
					if(!cvisible || visible) {
						if(ocean.choppy_scale>0) {
							if(moreAccurate) {
								if(tack==0) rrigidbody.AddForceAtPosition (-Vector3.left * (ocean.GetChoppyAtLocation2Fast() * ChoppynessFactor*Random.Range(0.5f,1.3f))*fact2, cpos);
								else rrigidbody.AddForceAtPosition (-Vector3.left * (ocean.GetChoppyAtLocation2Fast() * ChoppynessFactor*Random.Range(0.5f,1.3f))*fact2, transform.position);
							} else {
								if(tack==0) rrigidbody.AddForceAtPosition (-Vector3.left * (ocean.GetChoppyAtLocationFast() * ChoppynessFactor*Random.Range(0.5f,1.3f))*fact2, cpos);
								else rrigidbody.AddForceAtPosition (-Vector3.left * (ocean.GetChoppyAtLocationFast() * ChoppynessFactor*Random.Range(0.5f,1.3f))*fact2, transform.position);
							}
						}
					}
				}
				//if the object gets its position affected by the wind. Useful for smaller objects).
				if(WindAffectsPosition) {
					if(!wvisible || visible) {
						if(tack==1) rrigidbody.AddForceAtPosition(new Vector3(ocean.pWindx, 0 , ocean.pWindy) * WindFactor*fact2, cpos);
						else rrigidbody.AddForceAtPosition(new Vector3(ocean.pWindx, 0 , ocean.pWindy) * WindFactor*fact2, transform.position);
					}
				}
			}

			//the object will slide down a steep wave
			//modify it to your own needs since it is a quick and dirty method.
			if(xAngleAddsSliding) {
				if(!svisible || visible) {
					float xangle = transform.localRotation.eulerAngles.x;
					currAngleX = (int)xangle;

					if(prevAngleX != currAngleX) {

						float fangle=0f;

						if(xangle>270 && xangle<355) {
							fangle = (360-xangle)*0.1f;
							accel -= fangle* slideFactor; if(accel<-20) accel=-20;
							}

						if(xangle>5 && xangle<90) {
							fangle = xangle*0.1f;
							accel += fangle* slideFactor;  if(accel>20) accel=20;
						}

						prevAngleX = currAngleX;
					}

					if((int)accel!=0) rrigidbody.AddRelativeForce (Vector3.forward * accel, ForceMode.Acceleration);
					if(accel>0) { accel-= 0.05f;	if(accel<0) accel=0; }
					if(accel<0) { accel+= 0.05f; if(accel>0) accel=0; }
				}
			}

		}
	}

	public void Sink(bool isActive)	{ sink = isActive; }

	static float Lerp (float from, float to, float value) {
		if (value < 0.0f) return from;
		else if (value > 1.0f) return to;
		return (to - from) * value + from;
	}

}

在Unity编辑器中的表现如下所示:


运行效果如下所示:

效果非常绚丽。。。。。。。。

第四步,除了LOD海水网格渲染外,还提供了整个海水平面的渲染,以及可视化的界面操作,效果如下所示:

在该操作界面中有OceanMaterial材质对应的Shader脚本,在这里就不一一列举了。

第五步,多线程渲染主要是用于初始化海水网格以及算法的计算:

uocean.setThreads(2);
			if(SystemInfo.processorCount == 1) uocean.setThreads(1);
			//--------------------------------------------------------------------------------------------------------------------------------------------
			uocean.UoceanInit(width, height, pWindx, pWindy, speed, waveScale, choppy_scale, size.x, size.y, size.z, waveDistanceFactor);
			uocean._calcComplex(data, t_x, Time.time, 0, height);
			uocean._fft1(data);
			uocean._fft2(t_x);
			uocean._calcPhase3(data, t_x, vertices, baseHeight, normals, tangents, reflectionRefractionEnabled, canCheckBuoyancyNow, waveScale);

其他的读者可以通过Demo去查看。

代码链接地址:  http://pan.baidu.com/s/1pK91RYV  密码:xdx6


Unity3D 海水多线程渲染算法实现的更多相关文章

  1. Unity3d 基于物理渲染Physically-Based Rendering之最终篇

    前情提要: 讲求基本算法 Unity3d 基于物理渲染Physically-Based Rendering之specular BRDF plus篇 Unity3d 基于物理渲染Physically-B ...

  2. BGFX多线程渲染

    BGFX多线程渲染 1. 多线程基础 1. 并发概念 1. 并发任务简介 在多年以前,在手机.pc端.游戏机等,都是一个单核的CPU.这样,在硬件层面上,处理多个任务的时候,也是把一些任务切分成一些小 ...

  3. Unity4、Unity5移动平台多线程渲染在部分安卓手机上会造成闪退

    你看到的crash堆栈可能是这样的: SIGSEGV(SEGV_MAPERR)   #00  pc 0001276c                          /system/lib/libc ...

  4. HTML5触屏版多线程渲染模板技术分享

    前言: 了解js编译原理的屌丝们都知道,js是单线程的,想当年各路神仙为了实现js的多线程,为了解决innerHTML输出大段HTML卡页面的顽疾,纷纷设计了诸如假冒的“多线程“实现,我自己也在写开源 ...

  5. Unity3D ShaderLab 修改渲染队列进行深度排序

    Unity3D ShaderLab 修改渲染队列进行深度排序 为了更深刻的理解透明度,我们还需要学习一下深度排序,简单来说就是物体被渲染的先后顺序. Unity允许我们通过代码来控制某个特定物体渲染到 ...

  6. Unity3d的批渲染 batch rendering

    http://blog.csdn.net/leonwei/article/details/41942157 批渲染(Batch) batch render 是大部分引擎提高渲染效率的方法,基本原理就是 ...

  7. DirectX* 11 多线程渲染的性能、方法和实践

    对于在 CPU 上运行的 PC 游戏,渲染通常是主要的性能瓶颈:多线程渲染是一种消除瓶颈的有效方法.本文研究了 DirectX* 11 多线程渲染的性能可扩展性,讨论了多线程渲染的两种基本方法,并介绍 ...

  8. 《图解UE4渲染体系》Part 1 多线程渲染

    上回书<Part 0 引擎基础>说到,我们粗略地知道UE4是以哪些类来管理一个游戏场景里的数据的,但这仅仅是我们开始探索UE4渲染体系的一小步. 本回主要介绍UE4渲染体系中比较宏观顶层的 ...

  9. Unity3d三大光照渲染介绍

      重要:在目前市面上常见的游戏引擎中,主要采用以下三种灯光实现方式: 顶点照明渲染路径细节 Vertex Lit Rendering Path Details 正向渲染路径细节 Forward Re ...

随机推荐

  1. 2017年4月16日 一周AnswerOpenCV佳作赏析

    2017年4月16日 一周AnswerOpenCV佳作赏析 1.HelloHow to smooth edge of text in binary image, based on threshold. ...

  2. zabbix3.2通过snmp v2采集Dell服务器iDRAC口信息监控硬件

    模板下载 https://files.cnblogs.com/files/LuckWJL/zbx_export_templates.xml 模板源代码 <?xml version="1 ...

  3. The Startup Manager FAQ

    Main Features: 1.  Login Items: Manageable list of applications that are launched automatically ever ...

  4. linux下nano中复制粘贴剪切的快捷键是什么

    答: 1.复制:alt+6 2.剪切:ctrl+k 3.粘贴:ctrl+u 4.自由剪切: ctrl+6指定起始剪切位置,按上下左右键来选中内容,然后按下ctrl+k即可自由剪切 5.撤销: alt+ ...

  5. Student : IComparable<Student> 以及逆变和协变

    IComparable<Student>是Student的父类,所以IComparable<Student>可以接收Student.但是在使用CompareTo方法的时候,必须 ...

  6. vue.js中路由传递参数

    知识点:vue路由传递参数,第二个页面(A.B页面)拿到参数,使用参数 方法一:使用 <router-link :to="{name:'edithospital',params:{hi ...

  7. 浅谈const限定符

    什么是const限定符? Const限定符是我们通常所说的常量限定符,被const修饰的对象具有常量性质,只能读,不能写. 为什么使用const限定符? 用const变量取代“魔数”,代码更容易理解和 ...

  8. 拓扑排序 - hdu 1285(普通和优先队列优化)

    2017-09-12 19:50:58 writer:pprp 最近刚开始接触拓扑排序,拓扑排序适用于:无圈图的顶点的一种排序, 用来解决有优先级别的排序问题,比如课程先修后修,排名等. 主要实现:用 ...

  9. 如何制作自己的R包?

    摘自 方匡南 等编著<R数据分析-方法与案例详解>.电子工业出版社 R包简介 R包提供了一个加载所需代码.数据和文件的集合.R软件自身就包含大约30种不同功能的包,这些基本包提供了R软件的 ...

  10. Java循环跳转语句之 continue

    continue 的作用是跳过循环体中剩余的语句执行下一次循环. 例如,打印 1--10 之间所有的偶数,使用 continue 语句实现代码为: 运行结果: continue与break的区别就是, ...