void Awake() { if (!VerifyRequirements()) { enabled = false; return; } Instance = this; Scale = Mathf.Clamp(Scale, _minScale, _maxScale); OceanBuilder.GenerateMesh(this, _lodDataResolution, _geometryDownSampleFactor, _lodCount); if (null == GetComponent <BuildCommandBufferBase>()) { gameObject.AddComponent <BuildCommandBuffer>(); } InitViewpoint(); InitTimeProvider(); if (_attachDebugGUI && GetComponent <OceanDebugGUI>() == null) { gameObject.AddComponent <OceanDebugGUI>(); } }
/// <summary> /// Submit draws to create the Gerstner waves. LODs from 0 to N-2 render the Gerstner waves from their lod. Additionally, any waves /// in the biggest lod, or too big for the biggest lod, are rendered into both of the last two LODs N-1 and N-2, as this allows us to /// move these waves between LODs without pops when the camera changes heights and the LODs need to change scale. /// </summary> public void BuildCommandBuffer(int lodIdx, OceanRenderer ocean, CommandBuffer buf) { var lodCount = ocean.CurrentLodCount; // LODs up to but not including the last lod get the normal sets of waves if (lodIdx < lodCount - 1 && _drawLOD[lodIdx]) { if (_rasterMesh) { buf.DrawMesh(_rasterMesh, Matrix4x4.identity, _materials[lodIdx]); } } // The second-to-last lod will transition content into it from the last lod if (lodIdx == lodCount - 2 && _drawLODTransitionWaves) { buf.DrawMesh(_rasterMesh, Matrix4x4.identity, _materialBigWaveTransition); } // Last lod gets the big wavelengths if (lodIdx == lodCount - 1 && _drawLOD[lodIdx]) { buf.DrawMesh(_rasterMesh, Matrix4x4.identity, _materials[OceanRenderer.Instance.CurrentLodCount - 1]); } }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); // if there is nothing in the scene tagged up for depth rendering, and we have cleared the RTs, then we can early out var drawList = RegisterLodDataInputBase.GetRegistrar(GetType()); if (drawList.Count == 0 && _targetsClear) { return; } for (int lodIdx = OceanRenderer.Instance.CurrentLodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets, 0, CubemapFace.Unknown, lodIdx); buf.ClearRenderTarget(false, true, Color.black); buf.SetGlobalInt(sp_LD_SliceIndex, lodIdx); SubmitDraws(lodIdx, buf); } // targets have now been cleared, we can early out next time around if (drawList.Count == 0) { _targetsClear = true; } }
// 変更 public void SetOcean(int num, OceanRenderer ocean) { // 最初 Debug.Log("最初"); oceans[num] = ocean; Debug.Log("num:" + num); }
void Start() { _instance = this; _oceanBuilder = FindObjectOfType <OceanBuilder>(); _oceanBuilder.GenerateMesh(MakeBuildParams()); }
void Awake() { if (!VerifyRequirements()) { enabled = false; return; } #if UNITY_EDITOR if (_verifySRPVersionInEditor) { // Fire off a request to get the URP version, as early versions are not compatible _request = Client.List(true, false); } #endif Instance = this; Scale = Mathf.Clamp(Scale, _minScale, _maxScale); OceanBuilder.GenerateMesh(this, _lodDataResolution, _geometryDownSampleFactor, _lodCount); if (null == GetComponent <BuildCommandBufferBase>()) { gameObject.AddComponent <BuildCommandBuffer>(); } InitViewpoint(); InitTimeProvider(); if (_attachDebugGUI && GetComponent <OceanDebugGUI>() == null) { gameObject.AddComponent <OceanDebugGUI>(); } }
public bool Validate(OceanRenderer ocean) { if (_type == OceanDepthCacheType.Baked) { if (_savedCache == null) { Debug.LogError("Validation: Depth cache type is 'Saved Cache' but no saved cache data is provided. Click this message to highlight the cache in question.", this); } } else { if ((_layerNames == null || _layerNames.Length == 0)) { Debug.LogError("Validation: No layers specified for rendering into depth cache, and no geometries manually provided. Click this message to highlight the cache in question.", this); } if (_forceAlwaysUpdateDebug) { Debug.LogWarning("Validation: Force Always Update Debug option is enabled on depth cache " + gameObject.name + ", which means it will render every frame instead of running from the cache. Click this message to highlight the cache in question.", this); } foreach (var layerName in _layerNames) { var layer = LayerMask.NameToLayer(layerName); if (layer == -1) { Debug.LogError("Invalid layer specified for objects/geometry providing the ocean depth: \"" + layerName + "\". Does this layer need to be added to the project (Edit/Project Settings/Tags and Layers)? Click this message to highlight the cache in question.", this); } } if (_resolution < 4) { Debug.LogError("Cache resolution " + _resolution + " is very low. Is this intentional? Click this message to highlight the cache in question.", this); } // We used to test if nothing is present that would render into the cache, but these could probably come from other scenes, and AssignLayer means // objects can be tagged up at run-time. } if (transform.lossyScale.magnitude < 5f) { Debug.LogWarning("Validation: Ocean depth cache transform scale is small and will capture a small area of the world. The scale sets the size of the area that will be cached, and this cache is set to render a very small area. Click this message to highlight the cache in question.", this); } if (Mathf.Abs(transform.position.y - ocean.transform.position.y) > 0.00001f) { Debug.LogWarning("Validation: It is recommended that the cache is placed at the same height (y component of position) as the ocean, i.e. at the sea level. If the cache is created before the ocean is present, the cache height will inform the sea level. Click this message to highlight the cache in question.", this); } var rend = GetComponentInChildren <Renderer>(); if (rend != null) { Debug.LogWarning("Validation: It is not expected that a depth cache object has a renderer component in its hierarchy. The cache is typically attached to an empty GameObject. Please refer to the example content.", rend); } return(true); }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); var lodCount = ocean.CurrentLodCount; float substepDt; int numSubsteps; GetSimSubstepData(ocean.DeltaTime, out numSubsteps, out substepDt); for (int stepi = 0; stepi < numSubsteps; stepi++) { Swap(ref _sources, ref _targets); _renderSimProperties.Initialise(buf, _shader, krnl_ShaderSim); _renderSimProperties.SetFloat(sp_SimDeltaTime, substepDt); _renderSimProperties.SetFloat(sp_SimDeltaTimePrev, _substepDtPrevious); // compute which lod data we are sampling source data from. if a scale change has happened this can be any lod up or down the chain. // this is only valid on the first update step, after that the scale src/target data are in the right places. var srcDataIdxChange = ((stepi == 0) ? ScaleDifferencePow2 : 0); // only take transform from previous frame on first substep var usePreviousFrameTransform = stepi == 0; // bind data to slot 0 - previous frame data ValidateSourceData(usePreviousFrameTransform); BindSourceData(_renderSimProperties, false, usePreviousFrameTransform, true); SetAdditionalSimParams(_renderSimProperties); buf.SetGlobalFloat(sp_LODChange, srcDataIdxChange); _renderSimProperties.SetTexture( sp_LD_TexArray_Target, DataTexture ); _renderSimProperties.DispatchShaderMultiLOD(); for (var lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets, _targets.depthBuffer, 0, CubemapFace.Unknown, lodIdx); SubmitDraws(lodIdx, buf); } _substepDtPrevious = substepDt; } // any post-sim steps. the dyn waves updates the copy sim material, which the anim wave will later use to copy in // the dyn waves results. for (var lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { BuildCommandBufferInternal(lodIdx); } }
public static Transform GenerateMesh(OceanRenderer ocean, List <OceanChunkRenderer> tiles, int lodDataResolution, int geoDownSampleFactor, int lodCount) { if (lodCount < 1) { Debug.LogError("Invalid LOD count: " + lodCount.ToString(), ocean); return(null); } int oceanLayer = LayerMask.NameToLayer(ocean.LayerName); if (oceanLayer == -1) { Debug.LogError("Invalid ocean layer: " + ocean.LayerName + " please add this layer.", ocean); oceanLayer = 0; } #if PROFILE_CONSTRUCTION System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); #endif ClearOutTiles(ocean, tiles); var root = new GameObject("Root"); Debug.Assert(root != null, "The ocean Root transform could not be immediately constructed. Please report this issue to the Crest developers via our support email or GitHub at https://github.com/wave-harmonic/crest/issues ."); root.hideFlags = ocean._hideOceanTileGameObjects ? HideFlags.HideAndDontSave : HideFlags.DontSave; root.transform.parent = ocean.transform; root.transform.localPosition = Vector3.zero; root.transform.localRotation = Quaternion.identity; root.transform.localScale = Vector3.one; if (!OceanRenderer.RunningHeadless && !OceanRenderer.RunningWithoutGPU) { // create mesh data Mesh[] meshInsts = new Mesh[(int)PatchType.Count]; Bounds[] meshBounds = new Bounds[(int)PatchType.Count]; // 4 tiles across a LOD, and support lowering density by a factor var tileResolution = Mathf.Round(0.25f * lodDataResolution / geoDownSampleFactor); for (int i = 0; i < (int)PatchType.Count; i++) { meshInsts[i] = BuildOceanPatch((PatchType)i, tileResolution, out meshBounds[i]); } for (int i = 0; i < lodCount; i++) { CreateLOD(ocean, tiles, root.transform, i, lodCount, meshInsts, meshBounds, lodDataResolution, geoDownSampleFactor, oceanLayer); } } #if PROFILE_CONSTRUCTION sw.Stop(); Debug.Log("Finished generating " + lodCount.ToString() + " LODs, time: " + (1000.0 * sw.Elapsed.TotalSeconds).ToString(".000") + "ms"); #endif return(root.transform); }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); var lodCount = OceanRenderer.Instance.CurrentLodCount; var steps = GetNumSubsteps(Time.deltaTime); var substepDt = Time.deltaTime / steps; for (int stepi = 0; stepi < steps; stepi++) { for (var lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { SwapRTs(ref _sources[lodIdx], ref _targets[lodIdx]); } for (var lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { _renderSimMaterial[stepi, lodIdx].SetFloat("_SimDeltaTime", substepDt); _renderSimMaterial[stepi, lodIdx].SetFloat("_SimDeltaTimePrev", _substepDtPrevious); _renderSimMaterial[stepi, lodIdx].SetFloat("_GridSize", OceanRenderer.Instance._lods[lodIdx]._renderData._texelWidth); // compute which lod data we are sampling source data from. if a scale change has happened this can be any lod up or down the chain. // this is only valid on the first update step, after that the scale src/target data are in the right places. var srcDataIdx = lodIdx + ((stepi == 0) ? ScaleDifferencePow2 : 0); // only take transform from previous frame on first substep var usePreviousFrameTransform = stepi == 0; if (srcDataIdx >= 0 && srcDataIdx < lodCount) { // bind data to slot 0 - previous frame data BindSourceData(srcDataIdx, 0, _renderSimMaterial[stepi, lodIdx], false, usePreviousFrameTransform); } else { // no source data - bind params only BindSourceData(lodIdx, 0, _renderSimMaterial[stepi, lodIdx], true, usePreviousFrameTransform); } SetAdditionalSimParams(lodIdx, _renderSimMaterial[stepi, lodIdx]); buf.Blit(null, DataTexture(lodIdx), _renderSimMaterial[stepi, lodIdx]); SubmitDraws(lodIdx, buf); } _substepDtPrevious = substepDt; } // any post-sim steps. the dyn waves updates the copy sim material, which the anim wave will later use to copy in // the dyn waves results. for (var lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { BuildCommandBufferInternal(lodIdx); } }
void Start() { _instance = this; _oceanBuilder = GetComponent <OceanBuilder>(); _oceanBuilder.GenerateMesh(MakeBuildParams()); SetSmoothLODsShaderParam(); }
public IFlowProvider CreateFlowProvider(OceanRenderer ocean) { // Flow is GPU only, and can only be queried using the compute path if (ocean._lodDataFlow != null) { return(new QueryFlow()); } return(new FlowProviderNull()); }
public void Validate(OceanRenderer ocean) { if ((_geometryToRenderIntoCache == null || _geometryToRenderIntoCache.Length == 0) && (_layerNames == null || _layerNames.Length == 0)) { Debug.LogError("Validation: No layers specified for rendering into depth cache, and no geometries manually provided. Click this message to highlight the cache in question.", this); } if (transform.lossyScale.magnitude < 5f) { Debug.LogWarning("Validation: Ocean depth cache transform scale is small and will capture a small area of the world. The scale sets the size of the area that will be cached, and this cache is set to render a very small area. Click this message to highlight the cache in question.", this); } if (_forceAlwaysUpdateDebug) { Debug.LogWarning("Validation: Force Always Update Debug option is enabled on depth cache " + gameObject.name + ", which means it will render every frame instead of running from the cache. Click this message to highlight the cache in question.", this); } if (Mathf.Abs(transform.position.y - ocean.transform.position.y) > 0.00001f) { Debug.LogWarning("Validation: It is recommended that the cache is placed at the same height (y component of position) as the ocean, i.e. at the sea level. If the cache is created before the ocean is present, the cache height will inform the sea level. Click this message to highlight the cache in question.", this); } var numObjectsFound = 0; foreach (var layerName in _layerNames) { var layer = LayerMask.NameToLayer(layerName); if (layer == -1) { Debug.LogError("Invalid layer specified: \"" + layerName + "\". Please specify valid layers for objects/geometry that provide the ocean depth. Click this message to highlight the cache in question.", this); } var renderers = FindObjectsOfType <MeshRenderer>(); foreach (var renderer in renderers) { if (renderer.gameObject.layer == layer) { numObjectsFound++; } } } if (numObjectsFound == 0 && _geometryToRenderIntoCache.Length == 0) { Debug.LogWarning("No objects will render into depth cache as there are no geometries specified, and no objects match the layer names. Click this message to highlight the cache in question.", this); } if (_resolution < 4) { Debug.LogError("Cache resolution " + _resolution + " is very low. Is this intentional? Click this message to highlight the cache in question.", this); } }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); // if there is nothing in the scene tagged up for depth rendering, and we have cleared the RTs, then we can early out var drawList = RegisterLodDataInputBase.GetRegistrar(GetType()); if (drawList.Count == 0 && _targetsClear) { return; } if (UseGeometryShader) { buf.SetRenderTarget(_targets, 0, CubemapFace.Unknown, -1); buf.ClearRenderTarget(false, true, Color.white * 1000f); Matrix4x4[] matrixArray = new Matrix4x4[MAX_LOD_COUNT]; var lt = OceanRenderer.Instance._lodTransform; for (int lodIdx = OceanRenderer.Instance.CurrentLodCount - 1; lodIdx >= 0; lodIdx--) { lt._renderData[lodIdx].Validate(0, this); Matrix4x4 platformProjectionMatrix = GL.GetGPUProjectionMatrix(lt.GetProjectionMatrix(lodIdx), true); Matrix4x4 worldToClipPos = platformProjectionMatrix * lt.GetWorldToCameraMatrix(lodIdx); matrixArray[lodIdx] = worldToClipPos; } buf.SetGlobalMatrixArray(sp_SliceViewProjMatrices, matrixArray); buf.SetGlobalInt(sp_CurrentLodCount, OceanRenderer.Instance.CurrentLodCount); foreach (var draw in drawList) { draw.Draw(buf, 1f, 0); } } else { for (int lodIdx = OceanRenderer.Instance.CurrentLodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets, 0, CubemapFace.Unknown, lodIdx); buf.ClearRenderTarget(false, true, Color.white * 1000f); buf.SetGlobalFloat(OceanRenderer.sp_LD_SliceIndex, lodIdx); SubmitDraws(lodIdx, buf); } } // targets have now been cleared, we can early out next time around if (drawList.Count == 0) { _targetsClear = true; } }
void Start() { _instance = this; _oceanBuilder = FindObjectOfType <OceanBuilder>(); _oceanBuilder.GenerateMesh(_baseVertDensity, _lodCount); if (_viewpoint == null) { _viewpoint = Camera.main.transform; } }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); Shader.SetGlobalFloat(sp_AttenuationInShallows, Settings.AttenuationInShallows); var lodCount = OceanRenderer.Instance.CurrentLodCount; // Validation for (int lodIdx = 0; lodIdx < OceanRenderer.Instance.CurrentLodCount; lodIdx++) { OceanRenderer.Instance._lodTransform._renderData[lodIdx].Validate(0, SimName); } foreach (var gerstner in _gerstners) { gerstner.CrestUpdate(buf); } // lod-dependent data _filterWavelength._lodCount = lodCount; for (int lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_waveBuffers, 0, CubemapFace.Unknown, lodIdx); buf.ClearRenderTarget(false, true, new Color(0f, 0f, 0f, 0f)); // draw any data with lod preference _filterWavelength._lodIdx = lodIdx; _filterWavelength._lodMaxWavelength = OceanRenderer.Instance._lodTransform.MaxWavelength(lodIdx); _filterWavelength._lodMinWavelength = _filterWavelength._lodMaxWavelength / 2f; _filterWavelength._globalMaxWavelength = OceanRenderer.Instance._lodTransform.MaxWavelength(OceanRenderer.Instance.CurrentLodCount - 1); SubmitDrawsFiltered(lodIdx, buf, _filterWavelength); } // Combine the LODs - copy results from biggest LOD down to LOD 0 if (_shapeCombinePassPingPong) { CombinePassPingPong(buf); } else { CombinePassCompute(buf); } // lod-independent data for (int lodIdx = lodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets, 0, CubemapFace.Unknown, lodIdx); // draw any data that did not express a preference for one lod or another SubmitDrawsFiltered(lodIdx, buf, _filterNoLodPreference); } }
public virtual void ApplySchedule(OceanRenderer ocean) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Flow camera renders first for (int i = 0; i < ocean.CurrentLodCount && ocean._camsFlow[i] != null; i++) { ocean._camsFlow[i].depth = -50 - i; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Dynamic waves camera renders second for (int i = 0; i < ocean.CurrentLodCount && ocean._camsDynWaves[i] != null; i++) { ocean._camsDynWaves[i].depth = -40 - i; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Animated waves next for (int i = 0; i < ocean.CurrentLodCount; i++) { ocean._camsAnimWaves[i].depth = -30 - i; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Copy dynamic waves into animated waves (convert to displacements in the process) if (ocean._camsDynWaves[i] != null) { ocean._camsDynWaves[i].GetComponent <LodDataDynamicWaves>().HookCombinePass(ocean._camsAnimWaves[i], CameraEvent.AfterForwardAlpha); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Do combine passes to carry long wavelengths/low detail up the chain into the high detail lods ocean._lodDataAnimWaves[0].HookCombinePass(ocean._camsAnimWaves[0], CameraEvent.AfterEverything); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Foam takes the final combined waves as input and generates foam for (int i = 0; i < ocean.CurrentLodCount && ocean._camsFoam[i] != null; i++) { ocean._camsFoam[i].depth = -20 - i; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- User cameras render the ocean surface at depth ~0 - above data should be ready to go! // warn if main camera scheduled early CheckMainCameraDepth(); }
public static Transform GenerateMesh(OceanRenderer ocean, int lodDataResolution, int geoDownSampleFactor, int lodCount) { if (lodCount < 1) { Debug.LogError("Invalid LOD count: " + lodCount.ToString(), ocean); return(null); } int oceanLayer = LayerMask.NameToLayer(ocean.LayerName); if (oceanLayer == -1) { Debug.LogError("Invalid ocean layer: " + ocean.LayerName + " please add this layer.", ocean); oceanLayer = 0; } #if PROFILE_CONSTRUCTION System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); #endif // create mesh data Mesh[] meshInsts = new Mesh[(int)PatchType.Count]; // 4 tiles across a LOD, and support lowering density by a factor var tileResolution = Mathf.Round(0.25f * lodDataResolution / geoDownSampleFactor); for (int i = 0; i < (int)PatchType.Count; i++) { meshInsts[i] = BuildOceanPatch((PatchType)i, tileResolution); } ClearOutTiles(ocean); var root = new GameObject("Root"); root.hideFlags = ocean._hideOceanTileGameObjects ? HideFlags.HideAndDontSave : HideFlags.DontSave; root.transform.parent = ocean.transform; root.transform.localPosition = Vector3.zero; root.transform.localRotation = Quaternion.identity; root.transform.localScale = Vector3.one; for (int i = 0; i < lodCount; i++) { CreateLOD(ocean, root.transform, i, lodCount, meshInsts, lodDataResolution, geoDownSampleFactor, oceanLayer); } #if PROFILE_CONSTRUCTION sw.Stop(); Debug.Log("Finished generating " + lodCount.ToString() + " LODs, time: " + (1000.0 * sw.Elapsed.TotalSeconds).ToString(".000") + "ms"); #endif return(root.transform); }
private void OnDisable() { #if UNITY_EDITOR // We don't run in "prefab scenes", i.e. when editing a prefab. Bail out if prefab scene is detected. if (PrefabStageUtility.GetCurrentPrefabStage() != null) { return; } #endif CleanUp(); Instance = null; }
public bool Validate(OceanRenderer ocean) { if (string.IsNullOrEmpty(_layerName)) { Debug.LogError("Validation: Layer name required by AssignLayer script. Click this error to see the script in question.", this); return(false); } if (LayerMask.NameToLayer(_layerName) < 0) { Debug.LogError("Validation: Layer " + _layerName + " does not exist in the project, please add it.", this); return(false); } return(true); }
// Velocity of the sphere, relative to the water. Computes on the fly, discards if teleport detected. Vector3 LateUpdateComputeVelRelativeToWater(OceanRenderer ocean) { Vector3 vel; var rnd = 1f + _noiseAmp * (2f * Mathf.PerlinNoise(_noiseFreq * ocean.CurrentTime, 0.5f) - 1f); // feed in water velocity vel = (transform.position - _posLast) / ocean.DeltaTimeDynamics; if (ocean.DeltaTimeDynamics < 0.0001f) { vel = Vector3.zero; } if (QueryFlow.Instance) { _sampleFlowHelper.Init(transform.position, _object.ObjectWidth); Vector2 surfaceFlow = Vector2.zero; _sampleFlowHelper.Sample(ref surfaceFlow); vel -= new Vector3(surfaceFlow.x, 0, surfaceFlow.y); } vel.y *= _weightUpDownMul; var speedKmh = vel.magnitude * 3.6f; if (speedKmh > _teleportSpeed) { // teleport detected vel *= 0f; if (_warnOnTeleport) { Debug.LogWarning("Teleport detected (speed = " + speedKmh.ToString() + "), velocity discarded.", this); } } else if (speedKmh > _maxSpeed) { // limit speed to max vel *= _maxSpeed / speedKmh; if (_warnOnSpeedClamp) { Debug.LogWarning("Speed (" + speedKmh.ToString() + ") exceeded max limited, clamped.", this); } } return(vel); }
public override void OnInspectorGUI() { base.OnInspectorGUI(); var target = this.target as OceanRenderer; if (GUILayout.Button("Rebuild Ocean")) { target.enabled = false; target.enabled = true; } if (GUILayout.Button("Validate Setup")) { OceanRenderer.RunValidation(target); } }
void Start() { _instance = this; _oceanBuilder = FindObjectOfType <OceanBuilder>(); _oceanBuilder.GenerateMesh(_baseVertDensity, _lodCount); _collProvider = new CollProviderDispTexs(); if (_cachedCpuOceanQueries) { _collProvider = new CollProviderCache(_collProvider); } if (_viewpoint == null) { _viewpoint = Camera.main.transform; } }
void BuildLodData(OceanRenderer ocean, CommandBuffer buf) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Ocean depths if (ocean._lodDataSeaDepths != null && ocean._lodDataSeaDepths.enabled) { ocean._lodDataSeaDepths.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Flow data if (ocean._lodDataFlow != null && ocean._lodDataFlow.enabled) { ocean._lodDataFlow.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Dynamic wave simulations if (ocean._lodDataDynWaves != null && ocean._lodDataDynWaves.enabled) { ocean._lodDataDynWaves.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Animated waves next if (ocean._lodDataAnimWaves != null && ocean._lodDataAnimWaves.enabled) { ocean._lodDataAnimWaves.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Foam simulation if (ocean._lodDataFoam != null && ocean._lodDataFoam.enabled) { ocean._lodDataFoam.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Clip surface if (ocean._lodDataClipSurface != null && ocean._lodDataClipSurface.enabled) { ocean._lodDataClipSurface.BuildCommandBuffer(ocean, buf); } }
public static void ClearOutTiles(OceanRenderer ocean) { if (ocean.Root == null) { return; } // Remove existing LODs for (int i = 0; i < ocean.Root.childCount; i++) { var child = ocean.Root.GetChild(i); if (child.name.StartsWith("Tile_L")) { DestroyGO(child); i--; } } DestroyGO(ocean.Root); }
void Awake() { if (!_primaryLight && _searchForPrimaryLightOnStartup) { _primaryLight = RenderSettings.sun; } if (!VerifyRequirements()) { enabled = false; return; } Instance = this; Scale = Mathf.Clamp(Scale, _minScale, _maxScale); // Resolution is 4 tiles across. var baseMeshDensity = _lodDataResolution * 0.25f / _geometryDownSampleFactor; // 0.4f is the "best" value when base mesh density is 8. Scaling down from there produces results similar to // hand crafted values which looked good when the ocean is flat. _lodAlphaBlackPointFade = 0.4f / (baseMeshDensity / 8f); // We could calculate this in the shader, but we can save two subtractions this way. _lodAlphaBlackPointWhitePointFade = 1f - _lodAlphaBlackPointFade - _lodAlphaBlackPointFade; OceanBuilder.GenerateMesh(this, _lodDataResolution, _geometryDownSampleFactor, _lodCount); if (null == GetComponent <BuildCommandBufferBase>()) { gameObject.AddComponent <BuildCommandBuffer>(); } InitViewpoint(); InitTimeProvider(); if (_attachDebugGUI && GetComponent <OceanDebugGUI>() == null) { gameObject.AddComponent <OceanDebugGUI>(); } }
void Build(OceanRenderer ocean, CommandBuffer buf) { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Ocean depths if (ocean._lodDataSeaDepths) { ocean._lodDataSeaDepths.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Flow data if (ocean._lodDataFlow) { ocean._lodDataFlow.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Dynamic wave simulations if (ocean._lodDataDynWaves) { ocean._lodDataDynWaves.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Animated waves next if (ocean._lodDataAnimWaves) { ocean._lodDataAnimWaves.BuildCommandBuffer(ocean, buf); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // --- Foam simulation if (ocean._lodDataFoam) { ocean._lodDataFoam.BuildCommandBuffer(ocean, buf); } }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); // if there is nothing in the scene tagged up for depth rendering, and we have cleared the RTs, then we can early out if (_drawList.Count == 0 && _targetsClear) { return; } for (int lodIdx = OceanRenderer.Instance.CurrentLodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets[lodIdx]); buf.ClearRenderTarget(false, true, Color.black); SubmitDraws(lodIdx, buf); } // targets have now been cleared, we can early out next time around if (_drawList.Count == 0) { _targetsClear = true; } }
public override void BuildCommandBuffer(OceanRenderer ocean, CommandBuffer buf) { base.BuildCommandBuffer(ocean, buf); // If there is nothing in the scene tagged up for depth rendering, and we have cleared the RTs, then we can early out var drawList = RegisterLodDataInputBase.GetRegistrar(GetType()); if (drawList.Count == 0 && _targetsClear) { return; } for (int lodIdx = OceanRenderer.Instance.CurrentLodCount - 1; lodIdx >= 0; lodIdx--) { buf.SetRenderTarget(_targets, 0, CubemapFace.Unknown, lodIdx); var defaultToClip = OceanRenderer.Instance._defaultClippingState == OceanRenderer.DefaultClippingState.EverythingClipped; buf.ClearRenderTarget(false, true, defaultToClip ? Color.white : Color.black); buf.SetGlobalInt(sp_LD_SliceIndex, lodIdx); SubmitDraws(lodIdx, buf); } // Targets are only clear if nothing was drawn _targetsClear = drawList.Count == 0; }
static void CreateLOD(OceanRenderer ocean, int lodIndex, int lodCount, Mesh[] meshData, int lodDataResolution, int geoDownSampleFactor, int oceanLayer) { float horizScale = Mathf.Pow(2f, lodIndex); bool isBiggestLOD = lodIndex == lodCount - 1; bool generateSkirt = isBiggestLOD && !ocean._disableSkirt; Vector2[] offsets; PatchType[] patchTypes; PatchType leadSideType = generateSkirt ? PatchType.FatXOuter : PatchType.SlimX; PatchType trailSideType = generateSkirt ? PatchType.FatXOuter : PatchType.FatX; PatchType leadCornerType = generateSkirt ? PatchType.FatXZOuter : PatchType.SlimXZ; PatchType trailCornerType = generateSkirt ? PatchType.FatXZOuter : PatchType.FatXZ; PatchType tlCornerType = generateSkirt ? PatchType.FatXZOuter : PatchType.SlimXFatZ; PatchType brCornerType = generateSkirt ? PatchType.FatXZOuter : PatchType.FatXSlimZ; if (lodIndex != 0) { // instance indices: // 0 1 2 3 // 4 5 // 6 7 // 8 9 10 11 offsets = new Vector2[] { new Vector2(-1.5f, 1.5f), new Vector2(-0.5f, 1.5f), new Vector2(0.5f, 1.5f), new Vector2(1.5f, 1.5f), new Vector2(-1.5f, 0.5f), new Vector2(1.5f, 0.5f), new Vector2(-1.5f, -0.5f), new Vector2(1.5f, -0.5f), new Vector2(-1.5f, -1.5f), new Vector2(-0.5f, -1.5f), new Vector2(0.5f, -1.5f), new Vector2(1.5f, -1.5f), }; // usually rings have an extra side of verts that point inwards. the outermost ring has both the inward // verts and also and additional outwards set of verts that go to the horizon patchTypes = new PatchType[] { tlCornerType, leadSideType, leadSideType, leadCornerType, trailSideType, leadSideType, trailSideType, leadSideType, trailCornerType, trailSideType, trailSideType, brCornerType, }; } else { // first LOD has inside bit as well: // 0 1 2 3 // 4 5 6 7 // 8 9 10 11 // 12 13 14 15 offsets = new Vector2[] { new Vector2(-1.5f, 1.5f), new Vector2(-0.5f, 1.5f), new Vector2(0.5f, 1.5f), new Vector2(1.5f, 1.5f), new Vector2(-1.5f, 0.5f), new Vector2(-0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(1.5f, 0.5f), new Vector2(-1.5f, -0.5f), new Vector2(-0.5f, -0.5f), new Vector2(0.5f, -0.5f), new Vector2(1.5f, -0.5f), new Vector2(-1.5f, -1.5f), new Vector2(-0.5f, -1.5f), new Vector2(0.5f, -1.5f), new Vector2(1.5f, -1.5f), }; // all interior - the "side" types have an extra skirt that points inwards - this means that this inner most // section doesn't need any skirting. this is good - this is the highest density part of the mesh. patchTypes = new PatchType[] { tlCornerType, leadSideType, leadSideType, leadCornerType, trailSideType, PatchType.Interior, PatchType.Interior, leadSideType, trailSideType, PatchType.Interior, PatchType.Interior, leadSideType, trailCornerType, trailSideType, trailSideType, brCornerType, }; } // debug toggle to force all patches to be the same. they'll be made with a surrounding skirt to make sure patches // overlap if (ocean._uniformTiles) { for (int i = 0; i < patchTypes.Length; i++) { patchTypes[i] = PatchType.Fat; } } // create the ocean patches for (int i = 0; i < offsets.Length; i++) { // instantiate and place patch var patch = new GameObject(string.Format("Tile_L{0}", lodIndex)); patch.layer = oceanLayer; patch.transform.parent = ocean.transform; Vector2 pos = offsets[i]; patch.transform.localPosition = horizScale * new Vector3(pos.x, 0f, pos.y); // scale only horizontally, otherwise culling bounding box will be scaled up in y patch.transform.localScale = new Vector3(horizScale, 1f, horizScale); patch.AddComponent <OceanChunkRenderer>().SetInstanceData(lodIndex, lodCount, lodDataResolution, geoDownSampleFactor); patch.AddComponent <MeshFilter>().mesh = meshData[(int)patchTypes[i]]; var mr = patch.AddComponent <MeshRenderer>(); // Sorting order to stop unity drawing it back to front. make the innermost 4 tiles draw first, followed by // the rest of the tiles by LOD index. all this happens before layer 0 - the sorting layer takes priority over the // render queue it seems! ( https://cdry.wordpress.com/2017/04/28/unity-render-queues-vs-sorting-layers/ ). This pushes // ocean rendering way early, so transparent objects will by default render afterwards, which is typical for water rendering. mr.sortingOrder = -lodCount + (patchTypes[i] == PatchType.Interior ? -1 : lodIndex); // I don't think one would use light probes for a purely specular water surface? (although diffuse foam shading would benefit) mr.lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off; mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; // arbitrary - could be turned on if desired mr.receiveShadows = false; // this setting is ignored by unity for the transparent ocean shader mr.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion; mr.material = ocean.OceanMaterial; // rotate side patches to point the +x side outwards bool rotateXOutwards = patchTypes[i] == PatchType.FatX || patchTypes[i] == PatchType.FatXOuter || patchTypes[i] == PatchType.SlimX || patchTypes[i] == PatchType.SlimXFatZ; if (rotateXOutwards) { if (Mathf.Abs(pos.y) >= Mathf.Abs(pos.x)) { patch.transform.localEulerAngles = -Vector3.up * 90f * Mathf.Sign(pos.y); } else { patch.transform.localEulerAngles = pos.x < 0f ? Vector3.up * 180f : Vector3.zero; } } // rotate the corner patches so the +x and +z sides point outwards bool rotateXZOutwards = patchTypes[i] == PatchType.FatXZ || patchTypes[i] == PatchType.SlimXZ || patchTypes[i] == PatchType.FatXSlimZ || patchTypes[i] == PatchType.FatXZOuter; if (rotateXZOutwards) { // xz direction before rotation Vector3 from = new Vector3(1f, 0f, 1f).normalized; // target xz direction is outwards vector given by local patch position - assumes this patch is a corner (checked below) Vector3 to = patch.transform.localPosition.normalized; if (Mathf.Abs(patch.transform.localPosition.x) < 0.0001f || Mathf.Abs(Mathf.Abs(patch.transform.localPosition.x) - Mathf.Abs(patch.transform.localPosition.z)) > 0.001f) { Debug.LogWarning("Skipped rotating a patch because it isn't a corner, click here to highlight.", patch); continue; } // Detect 180 degree rotations as it doesn't always rotate around Y if (Vector3.Dot(from, to) < -0.99f) { patch.transform.localEulerAngles = Vector3.up * 180f; } else { patch.transform.localRotation = Quaternion.FromToRotation(from, to); } } } }