Unity, Water Cube

Source: Internet
Author: User
Tags mul

In Monument Valley, a close begins with a treasure chest. There's a water cube inside, and there's a little fish on the swim. As follows:

Because we knew that "Monument Valley" was made by unity and now we are starting to learn unity, so we want to do a similar thing.

Unity5 standard assets inside there is a waterprodaytime, refraction and reflection have, began to think with it a spell on the finished son, can not think of this thing can only flat put effect is right, vertical put effect is wrong, as shown:

(Below the horizontal water water ripple is normal, but the above erect placed water the watermark becomes the vertical strip, this effect is not acceptable for the water cube that we want to implement)

It must have been the official realization of this effect. Subconsciously assume that we must be horizontally positioned when we use it. So I had to look at its implementation code, see where to use the "Horizontal placement hypothesis", found two places:

First place:

The following code is in the vertex shader in Fxwaterpro.shader:

Scroll Bump waves
FLOAT4 temp;
FLOAT4 Wpos = Mul (_object2world, V.vertex);
TEMP.XYZW = wpos.xzxz * _wavescale4 + _waveoffset;
O.bumpuv0 = Temp.xy;
O.bumpuv1 = Temp.wz;

This sentence: TEMP.XYZW = wpos.xzxz * _wavescale4 + _waveoffset; it uses the XZ component of the vertex world coordinates to generate the texture coordinates of the surface bump (bump) map, It is clearly assumed that the surface is horizontally placed (or at least the water surface is projected not to be 0 on the XZ plane).

The most universal method is to transfer the plane equation of water surface into the vertex shader, in which the projection coordinates of WPOS on this plane are calculated projpos, then the Projpos is transformed into local space projposinlocalspace, then TEMP.XYZW = PROJPOSINLOCALSPACE.XZXZ * _wavescale4 + _waveoffset; But wait, because Wpos is the vertex world coordinates of the water surface, so its projection projpos on the water is not wpos itself! Then convert the Projpos into a local space, that is, the wpos into the local space, but because the world vertex coordinates wpos is from the local vertex coordinates v.vertex, so wpos go to the local space is not the result of V.vertex it! So the final conclusion is that the first direct use of local coordinates V.vertex is OK. That is, the above code should read:

Scroll Bump waves
FLOAT4 temp;
FLOAT4 Wpos = V.vertex;//mul (_object2world, V.vertex);
TEMP.XYZW = wpos.xzxz * _wavescale4 + _waveoffset;
O.bumpuv0 = Temp.xy;
O.bumpuv1 = Temp.wz;

This is the correct code for any angle of the water.

Second place:

The following code is in the Water.cs:

Find out the reflection plane:position and normal in world space

Vector3 pos = transform.position;
Vector3 normal = Transform.up;

Optionally disable pixel lights for reflection/refraction
int oldpixellightcount = Qualitysettings.pixellightcount;
if (disablepixellights)
{
Qualitysettings.pixellightcount = 0;
}

Updatecameramodes (Cam, Reflectioncamera);
Updatecameramodes (Cam, Refractioncamera);

Render Reflection if needed
if (Mode >= watermode.reflective)
{
Reflect camera around reflection plane
Float d =-vector3.dot (Normal, POS)-clipplaneoffset;
Vector4 Reflectionplane = new Vector4 (normal.x, Normal.y, normal.z, D);

matrix4x4 reflection = Matrix4x4.zero;
Calculatereflectionmatrix (ref reflection, Reflectionplane);
Vector3 oldpos = cam.transform.position;
Vector3 Newpos = Reflection. Multiplypoint (Oldpos);
Reflectioncamera.worldtocameramatrix = Cam.worldtocameramatrix * reflection;

Setup Oblique projection matrix So, near plane are our reflection
Plane. This is the We clip everything below/above it for free.
Vector4 Clipplane = Cameraspaceplane (Reflectioncamera, POS, Normal, 1.0f);
Reflectioncamera.projectionmatrix = Cam. Calculateobliquematrix (Clipplane);

Reflectioncamera.cullingmask = ~ (1 << 4) & Reflectlayers.value; Never render water layer
Reflectioncamera.targettexture = m_reflectiontexture;
Gl.invertculling = true;
reflectionCamera.transform.position = Newpos;
Vector3 Euler = cam.transform.eulerAngles;
reflectionCamera.transform.eulerAngles = new Vector3 (-euler.x, Euler.y, euler.z);
Reflectioncamera.render ();
reflectionCamera.transform.position = Oldpos;
Gl.invertculling = false;
Getcomponent<renderer> (). Sharedmaterial.settexture ("_reflectiontex", m_reflectiontexture);
}

The following two sentences:

Vector3 Euler = cam.transform.eulerAngles;

ReflectionCamera.transform.eulerAngles = new Vector3 (-euler.x, Euler.y, Euler.z);

it means to set the reflex camera transform gesture to be symmetrical with the current camera transform attitude about the horizontal plane (XZ plane), so This sentence contains the "Horizontal placement hypothesis".

The general approach should be to let the reflective camera transform gesture with the current camera transform posture in relation to the reflective face, which can be calculated as follows:

1, calculate the pose matrix of the reflective camera:

Because the mirror matrix reflection has been found in the above code, the pose matrix of the reflection machine is:

Reflectcammatrix=reflection*cam.transform.localtoworldmatrix.

(Note: The Mirror matrix reflection is the same matrix as its inverse matrix, because the mirror image is itself.) )

2, the attitude matrix Reflectcamrotationmatrix is extracted from the position-pose matrix Reflectcammatrix.

3, the attitude Matrix Reflectcamrotationmatrix is converted to four yuan, and assigned to the transform.rotation of the reflective camera.

for information on extracting positions, gestures, and scales from a matrix, refer to: http://forum.unity3d.com/threads/how-to-assign-matrix4x4-to-transform.121966/

But do you really need to do this? See in the code above that there is such a sentence:

Reflectioncamera.worldtocameramatrix = Cam.worldtocameramatrix * reflection;

View Camera.worldtocameramatrix's documentation, wrote:

If you change this matrix, the camera no longer updates it rendering based on its Transform. This lasts until your call Resetworldtocameramatrix.

is to say, After the Reflectioncamera.worldtocameramatrix is set, the rendering will no longer press Reflectioncamera.transform to go (unless you call Reflectioncamera.resetworldtocameramatr IX). Therefore, it is superfluous and invalid to set the Reflectioncamera.transform later in the code, that is, the blue part of the code above is superfluous. As to why the author should write this code, I think it may be to reflect the rigorous thinking.

To simply verify our conclusion, you can delete the blue part of the code, or change it to something else, and then run it, and you can see that the rendering result is really unaffected.

So our final conclusion is: although reflectionCamera.transform.eulerAngles = new Vector3 (-euler.x, Euler.y, Euler.z), this code contains "horizontal placement assumptions", But because it doesn't work at all, the entire Water.cs script is still generic.

Also note that there are two sentences in the above code:

Vector3 normal = Transform.up;

Vector4 Reflectionplane = new Vector4 (normal.x, Normal.y, normal.z, D);

That is, it is the transform.up of the gameobject that hangs the Water.cs script as the normal of the reflector, which requires that the mesh patches that we add for this gameobject must be normal-facing before any transformations. If we want to tilt or erect the surface of the water, we should adjust the gameobject rotation to achieve.

Therefore, we cannot use the quad in unity as the mesh of the surface gameobject, because it is originally erected. So we can either use the modeling software to build an original, normal-upward patch model into unity, or create one of these patches directly in unity with a script, I'm using the latter, and the script that generates the patch is as follows:

(Generates a flat patch with normal y-axis positive direction)

Reference: http://www.cnblogs.com/wantnon/p/4522415.html

Using Unityengine;
Using System.Collections;
[Executeineditmode]
public class Genquadfacepy:monobehaviour {

void Awake () {
Gameobject.getcomponent<meshfilter> (). Mesh = Createmeshfacepy (n);
}
Mesh createmeshfacepy (float width, float height)
{

Mesh m = new mesh ();
M.name = "Quadfacepy";
Note:unity is left-hand system
M.vertices = new vector3[] {
New Vector3 (-WIDTH/2, 0,-HEIGHT/2),
New Vector3 (-WIDTH/2, 0, HEIGHT/2),
New Vector3 (WIDTH/2, 0, HEIGHT/2),
New Vector3 (WIDTH/2, 0,-HEIGHT/2)
};
M.UV = new vector2[] {
New Vector2 (0, 0),
New Vector2 (0, 1),
New Vector2 (1, 1),
New Vector2 (1, 0)
};
M.triangles = new int[] {0, 1, 2, 0, 2, 3};
M.recalculatenormals ();
M.recalculatebounds ();
return m;
}
}

Finally, there are two things to note:

1, The six water surfaces that make up the water cube must use six different material, so you can't share the same material.

The reasons are: the shader (fxwaterpro.shader) of the surface material refersto R eflectiontexture, while The reflectiontexture of the water surface 1 is rendered by the reflectioncamera of the water surface 1 . The reflectiontexture of the water Surface 2 is rendered by the reflectioncamera of the water Surface 2. Since the reflectioncamera of the water surface 1 is different from the reflectioncamera of the water surface 2, it is necessary to use two different camera, so the reflectiontexture are different. So if the water surface 1 and the water Surface 2 material with the same, their shader reference reflectiontexture can only be the same, which is different from the water surface 1 and 2 of the water surface of the reflectiontexture of the contradiction, So the water surface 1 and the water Surface 2 must use different matrial. (using Refractiontexture to analyze the same). (If you're curious about what happens to multiple water surfaces using the same matrial, you can do the experiment, and the result is a strange jump in the reflection and refraction effects of the surface when the camera rotates to an angle).

2,waterprodaytime has a bug under the orthographic camera.

For this bug, a separate log will be written in the future, here is a brief description:

For orthographic cameras, Unity will report the error "screen position out of view frustum" If water is in the viewport and the water plane's normal vector is perpendicular to the camera's line of sight. The error line is the Reflectioncamera.render () in the Water.cs script, but the analysis can be determined to be Reflectioncamera.projectionmatrix = cam. Calculateobliquematrix (Clipplane), more specifically Unity's camera.calculateobliquematrix the implementation of this API is bug/ Support for orthographic cameras is not perfect.

Since this is a bug in Unity's own API, the perfect solution is to wait for Unity's official fix. But work around should be there, wait for me to test well.

Unity, Water Cube

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.