笔者介绍:姜雪伟,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. powershell 设置环境变量 -- go 单元测试 exit status 3221225781

    执行单元测试时出错 go test -run TestImage 错误提示如下: exit status 3221225781 这个错误的意思是需要加载对应的库文件找不到,加载对应的库文件就习. 但是 ...

  2. git gc内存错误的解决方案

    Auto packing the repository for optimum performance. You may alsorun "git gc" manually. Se ...

  3. LeetCode—— Partition Equal Subset Sum

    Question Given a non-empty array containing only positive integers, find if the array can be partiti ...

  4. 【jdk源码分析】java多线程开启的三种方式

    1.继承Thread类,新建一个当前类对象,并且运行其start()方法 package com.xiaostudy.thread; /** * @desc 第一种开启线程的方式 * @author ...

  5. Javase、Javaee、Javame的区别

    /*简而言之,javase是基础,要先学,javaee是企业级,接着学,然后可以转到javaweb方向,javame是做移动应用的.基础必须先学,然后再考虑下一步*/ Java 平台有三个版本,这使软 ...

  6. You only look once

    计算MAP https://www.zhihu.com/question/53405779 http://tarangshah.com/blog/2018-01-27/what-is-map-unde ...

  7. spring @RequestMapping注解技巧

    @RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一.这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理方法上. 下面我们看看,@Request ...

  8. java的时间获取

    System类代表系统,系统级的很多属性和控制方法都放置在该类的内部.该类位于java.lang包. currentTimeMillis方法 public static long currentTim ...

  9. JS组件系列——基于Bootstrap Ace模板的菜单和Tab页效果

    Ace模板地址:http://code.google.com/p/ace-engine/wiki/AceTemplate(有时会打不开) Ace英文官网:http://wrapbootstrap.co ...

  10. C++ 线程的创建、挂起、唤醒和结束 &&&& 利用waitForSingleObject 函数陷入死锁的问题解决

    最近在写一个CAN总线的上位机软件,利用CAN转USB的设备连到电脑上,进行数据的传输.在接收下位机发送的数据的时候采用的在线程中持续接收数据. 1.在连接设备的函数中,开启线程. ,CREATE_S ...