-->

分類

2016年9月7日 星期三

Unity 2d wave-like motion mesh

reference

this needs one script attached to an empty GameObject and one material with texture

result video




public class WaveMesh : MonoBehaviour {
    private Mesh m_Mesh;
    private float size = 0.5f;
    private int gridSize = 8;
    public float waveFrequency = 5.0f;
    public float sizeScale = 0.5f;
    // Use this for initialization
    void Start () {
 
 }
 
 // Update is called once per frame
 void Update () {
        int dotsPerRow = gridSize + 1;
        int halfGridSize = gridSize / 2;
        Vector3[] verts = m_Mesh.vertices;
        float time = Time.time;
        for (int i = 0; i < dotsPerRow; i++)
        {
            for (int j = 0; j < dotsPerRow; j++)
            {
                int xIndex = j - halfGridSize;
                int yIndex = halfGridSize - i;
                float xSign = Mathf.Sign(xIndex);
                float ySign = Mathf.Sign(yIndex);
                float xPosTarget = xIndex;
                float yPosTarget = yIndex;
                //if (xIndex != 0 && yIndex != 0)
                {
                    Vector2 wave = waveCoord(xIndex, yIndex, halfGridSize, time);
                    verts[i * dotsPerRow + j] = new Vector3(wave.x, wave.y, 0.0f);
                }           
            }
        }
        m_Mesh.vertices = verts;
    }

    private Vector2 waveCoord(float xIndex, float yIndex, float halfGridSize, float time)
    {
        Vector2 p = new Vector2(xIndex, yIndex);
        
        p = p * sizeScale;
        float len = p.magnitude;
        float sincValue = sinc(p.magnitude * 0.5f);
        float temp = Mathf.Cos(time * waveFrequency - len / sizeScale) * 0.5f * sizeScale * sincValue;
        Vector2 offset = new Vector2(Mathf.Sign(xIndex) * temp, Mathf.Sign(yIndex) * temp);
        return offset + p;
    }

    private float clampTimeToPeriod(float time, float period)
    {
        return time - Mathf.Floor(time / period);
    }

    private float sinc(float x)
    {
        if(Mathf.Abs(x) < 0.0001f)
        {
            return Mathf.Sin(x);
        }
        return Mathf.Sin(x) / x;
    }

    void Awake()
    {
        GameObject plane = new GameObject("CreatedWaveMesh");
        MeshFilter meshFilter = (MeshFilter)plane.AddComponent(typeof(MeshFilter));
        meshFilter.mesh = CreateMesh();
        m_Mesh = meshFilter.mesh;
        MeshRenderer renderer = plane.AddComponent(typeof(MeshRenderer)) as MeshRenderer;
        //Load your material here
        Material newMat = Resources.Load("Materials/WaveStandardMaterial", typeof(Material)) as Material;
        renderer.material = newMat;
        //plane.transform.localScale = new Vector3(desiredSize / gridSize, desiredSize / gridSize, -1);
        plane.transform.localScale = new Vector3(1, 1, -1);
        plane.transform.localPosition = new Vector3(0.64f, 0.38f, -3);
    }

    Mesh CreateMesh()
    {
        Mesh m = new Mesh();
        m.name = "ScriptedMesh";
        /*
        case for gridSize == 4, there are 4x4=16 grids, dotsPerRow is 4+1=5
        .....
        .....
        .....
        .....
        .....
        */
        int dotsPerRow = gridSize + 1;
        int halfGridSize = gridSize / 2;
        Vector3[] verts = new Vector3[dotsPerRow * dotsPerRow];
        for (int i = 0; i < dotsPerRow; i++)
        {
            for(int j = 0; j < dotsPerRow; j++)
            {
                verts[i * dotsPerRow + j] = new Vector3(j - halfGridSize, halfGridSize - i, 0.0f);
            }
        }
        m.vertices = verts;

        Vector2[] uvs = new Vector2[verts.Length];
        float dotsPerRowf = (float)dotsPerRow;
        for (int i = 0; i < dotsPerRow; i++)
        {
            for (int j = 0; j < dotsPerRow; j++)
            {
                uvs[i * dotsPerRow + j] = new Vector2((float)j / dotsPerRowf, 1.0f - (float)i / dotsPerRowf);
            }
        }
        m.uv = uvs;

        int numTris = gridSize * gridSize * 2;
        int[] tris = new int[numTris * 3];
        
        for(int i = 0; i < gridSize; i++)
        {
            for(int j = 0; j < gridSize; j++)
            {
                int startIndex = 6 * (i * gridSize + j);
                tris[startIndex] = i * dotsPerRow + j;
                tris[startIndex + 1] = (i + 1) * dotsPerRow + j;
                tris[startIndex + 2] = i * dotsPerRow + j + 1;
                tris[startIndex + 3] = (i + 1) * dotsPerRow + j;
                tris[startIndex + 4] = (i + 1) * dotsPerRow + j + 1;
                tris[startIndex + 5] = i * dotsPerRow + j + 1;
            }
        }
        m.triangles = tris;
        m.RecalculateNormals();

        return m;
    }
}

2016年8月25日 星期四

Android Local unit test/Instrumentation Test

reference 1
reference for unit test

basic:

differences between Local unit test and Instrumentation Test:
Local unit test: can just run on JVM, doesn't need Android framework
Instrumentation Test: can test Android component(Activity, Service...) and UI

create Class for testing(both local unit test and instrumentation test) in app/src/androidTest/java/<package_name>

set build.gradle:
dependencies {
    androidTestCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:0.4'
    // Set this dependency to use JUnit 4 rules
    androidTestCompile 'com.android.support.test:rules:0.4'
    androidTestCompile 'com.android.support:support-annotations:23.1.1'
}

mokito for mocking context seems useless if we use the sample code snippet here (the Context is null and MockitoAnnotations.initMocks can't work

sample Local unit test

import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class MyUnitTest {
    @Test
    public void testOne() {

        assertTrue("123".equals("123"));
    }
}


sample Instrumentation test
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.widget.Toast;

//JUnit4
public class ActivityFunctionTest extends ActivityInstrumentationTestCase2 {
    public ActivityFunctionTest(Class activityClass) {
        super(activityClass);
    }

    public ActivityFunctionTest(){
        super(MainMenuAcitivity.class);
    }

    public void testSetText() throws Exception {

        // set text
        getActivity().runOnUiThread(new Runnable() {

            @Override
            public void run() {
                MainMenuAcitivity activity = getActivity();
                Toast.makeText(activity, "test toast 2", Toast.LENGTH_SHORT).show();
            }
        });

        getInstrumentation().waitForIdleSync();

    }

    @UiThreadTest
    public void testSetTextWithAnnotation() throws Exception {

        MainMenuAcitivity activity = getActivity();
        Toast.makeText(activity, "test toast", Toast.LENGTH_SHORT).show();

    }
}

monkey command:
adb shell monkey -p <package_name> -v <number_of_events>

2016年7月25日 星期一

Android view static inner class in layout

    <view xmlns:android="http://schemas.android.com/apk/res/android"
          class="com.package.OuterClassName$InnerViewClassName">
</view>

2016年7月19日 星期二

Unity failed wireframe shader

tried to assign uv for each vertex of triangle as (0,0), (0,1), (1,0) then use interpolated uv value in fragment shader to determine if it should be drawn as edge of the triangle

C# script:


using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class SetTriangleVertUV4ForWireframe : MonoBehaviour {
    private enum UVType
    {
        none,
        t00,
        t01,
        t10
    }
    private Mesh mMesh;
    private int[] triangleIndices;
    private Vector2[] mUV;
    // Use this for initialization
    void Start()
    {
        mMesh = GetComponent().mesh;
        triangleIndices = mMesh.triangles;
        mUV = new Vector2[mMesh.vertices.Length];
        for (int i = 0; i < mUV.Length; i++)
        {
            //UVType.none
            mUV[i] = new Vector2(-1.0f, -1.0f);
        }
        for (int i = 0; i < triangleIndices.Length; i += 3)
        {
            Vector2 v0 = mUV[triangleIndices[i]];
            Vector2 v1 = mUV[triangleIndices[i + 1]];
            Vector2 v2 = mUV[triangleIndices[i + 2]];
            HashSet uvTypeSet = new HashSet();
            HashSet vertIndexSet = new HashSet();
            uvTypeSet.Add(UVType.t00);
            uvTypeSet.Add(UVType.t01);
            uvTypeSet.Add(UVType.t10);
            vertIndexSet.Add(i);
            vertIndexSet.Add(i + 1);
            vertIndexSet.Add(i + 2);

            Dictionary uvTypeDic = new Dictionary();
            addUVType(uvTypeDic, v0, i);
            addUVType(uvTypeDic, v1, i + 1);
            addUVType(uvTypeDic, v2, i + 2);


            foreach (KeyValuePair pair in uvTypeDic)
            {
                UVType type = pair.Key;
                if (uvTypeSet.Contains(type))
                {
                    uvTypeSet.Remove(type);
                    vertIndexSet.Remove(pair.Value);
                }
            }

            //uvTypeSet now contains UVType that are not used
            List resultUVType = new List();
            List resultIndices = new List();
            foreach (UVType type in uvTypeSet)
            {
                resultUVType.Add(type);
            }
            resultUVType.Sort();
            foreach (int index in vertIndexSet)
            {
                resultIndices.Add(index);
            }
            resultIndices.Sort();
            //convert unused UVType to Vector2 as UV coord and save to uv4
            for (int j = 0; j < resultIndices.Count; j++)
            {
                mUV[triangleIndices[resultIndices[j]]] = buildVector2ByUVType(resultUVType[j]);
            }
        }
        mMesh.uv4 = mUV;
    }

    private Vector2 buildVector2ByUVType(UVType type)
    {
        switch (type)
        {
            case UVType.t00:
                return new Vector2(0.0f, 0.0f);
            case UVType.t01:
                return new Vector2(0.0f, 1.0f);
            case UVType.t10:
                return new Vector2(1.0f, 0.0f);
            default:
                return new Vector2(-1.0f, -1.0f);
        }
    }

    private void addUVType(Dictionary dic, Vector2 v, int index)
    {
        UVType uvType = checkUVType(v);
        if (uvType != UVType.none && !dic.ContainsKey(uvType))
        {
            dic.Add(uvType, index);
        }
    }

    private UVType checkUVType(Vector2 v)
    {
        if (Mathf.Approximately(v.x, -1.0f))
        {
            return UVType.none;
        }
        if (Mathf.Approximately(v.x, 1.0f))
        {
            return UVType.t10;
        }
        if (Mathf.Approximately(v.y, 1.0f))
        {
            return UVType.t01;
        }
        return UVType.t00;
    }
}


Shader code:

Shader "Unlit/TestWireframeUV4"
{
 Properties
 {
  _MainTex ("Texture", 2D) = "white" {}
 }
 SubShader
 {
  Tags { "RenderType"="Opaque" }
  LOD 100

  Pass
  {
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag
   // make fog work
   #pragma multi_compile_fog
   
   #include "UnityCG.cginc"

   struct appdata
   {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
    float2 uv4 : TEXCOORD3;
   };

   struct v2f
   {
    float2 uv : TEXCOORD0;
    float2 uv4 : TEXCOORD3;
    float4 vertex : SV_POSITION;
   };

   sampler2D _MainTex;
   float4 _MainTex_ST;
   
   v2f vert (appdata v)
   {
    v2f o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    o.uv4 = v.uv4;
    return o;
   }
   
   fixed4 frag (v2f i) : SV_Target
   {
    fixed4 col = tex2D(_MainTex, i.uv);
    if (length(i.uv4) > 0.7 && length(i.uv4) < 0.9) {
     //treated as edge of triangle
     return fixed4(0, 0, 0, 1);
    }
    return col;
   }
   ENDCG
  }
 }
}


result:
as you can see, some triangles don't have correct UV(at the top of the sphere). This is because sometimes the UV coordinates have conflicts in one triangle (e.g. (1,0), (1,0) for two vertices, in that case the second UV coordinate is discarded)




2016年7月5日 星期二

some old stuff


bump mapping by GLSL, but I didn't understance the relations between tangent, normal and binormal that time...


an image rendered by ray tracing, totally forgot  how I made photon mapping work...


3D drawing on Android


2016年6月19日 星期日

Unity 2d water blob

references:
[1] http://codeartist.mx/tutorials/liquids/

based on [1], I made a shader that combines Unity default sprite shader and the metaball calculation. This shader is used on Sprite of water particle, not on the Render Textures as the implementation of [1]

here is the result video


shader code:

Shader "2D/SpriteMetaball"
{
 Properties
 {
  _MainTex ("Texture", 2D) = "white" {}
  _Color("Tint Color", Color) = (1,1,1,1)
  _EdgeThreshold("EdgeThreshold", Range(0,1)) = 0.2
  [MaterialToggle] PixelSnap("Pixel snap", Float) = 0
  _CenterSaturation("CenterSaturation", Range(1,100)) = 2
  _StepSize("StepSize", Range(0.1,1)) = 1
 }
 SubShader
 {
  Tags 
  {
   "Queue" = "Transparent"
   "IgnoreProjector" = "True"
   "RenderType" = "Transparent"
   "PreviewType" = "Plane"
   "CanUseSpriteAtlas" = "True"
  }

  Cull Off
  Lighting Off
  ZWrite Off
  Blend One OneMinusSrcAlpha

  Pass
  {
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag
   #pragma multi_compile DUMMY PIXELSNAP_ON
   #pragma shader_feature ETC1_EXTERNAL_ALPHA
   
   #include "UnityCG.cginc"

   struct appdata
   {
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float2 uv : TEXCOORD0;
   };

   struct v2f
   {
    float2 uv : TEXCOORD0;
    fixed4 color : COLOR;
    float4 vertex : SV_POSITION;
   };

   sampler2D _MainTex;
   float4 _MainTex_ST;
   //tint color
   float4 _Color;
   //when texture color's alpha is below _EdgeThreshold, that color is discarded and the edge of rendered result formed
   //but it turns out that I got the result I want with _EdgeThreshold as 0...
   half _EdgeThreshold;
   /*
    higher _CenterSaturation makes the color from texture saturated faster(easier to become 1),
    since the default sprite texture this shader used has higher alpha in the center, 
    this value determined the how much the center part of rendered result is saturated 
   */
   half _CenterSaturation;
   //I think this value originally is related to metaball isosurface calculation,
   //but it may not be so useful in current implementation since I multiply texture's alpha which is formed as radial gradient 
   half _StepSize;

   sampler2D _AlphaTex;

   v2f vert (appdata v)
   {
    v2f o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    #ifdef PIXELSNAP_ON
    o.vertex = UnityPixelSnap(o.vertex);
    #endif
    return o;
   }

   float4 SampleSpriteTexture(float2 uv)
   {
    float4 color = tex2D(_MainTex, uv);

#if ETC1_EXTERNAL_ALPHA
    // get the color from an external texture (usecase: Alpha support for ETC1 on android)
    color.a = tex2D(_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA

    return color;
   }
   
   float4 frag (v2f i) : SV_Target
   {
    float4 col = SampleSpriteTexture(i.uv);
    
    float4 finalColor = col;
    if (finalColor.a > _EdgeThreshold) {
     //tint color
     finalColor *= _Color;
     //metaball isosurface
     finalColor.rgb *= float3(floor(finalColor.r * _CenterSaturation)*_StepSize, floor(finalColor.g * _CenterSaturation)*_StepSize, floor(finalColor.b * _CenterSaturation)*_StepSize);
     //this line kind of makes the isosurface of metaball formed by floor function and _StepSize meaningless, but I think it makes the rendered result looks better    
     finalColor.rgb *= finalColor.a;
    }
    else {
     discard;
    }

    return finalColor;
   }
   ENDCG
  }
 }
 Fallback "VertexLit"
}


here is the texture used for the water particle's SpriteRenderer(it's white with alpha, so you might only see a blank block here, but there is an image)




here is the settings of water particle:
you need to create a material with the shader code
the WaterBlob Sprite is the white image above(renamed from "RadialGradient" to "WaterBlob" in my project)

2016年6月3日 星期五

Unity Android native crash debug

reference
1. unpack the .apk you build with zip or 7-zip, assume it unpack to the folder "YourProject"
2. copy "YourProject/lib/armeabi-v7a/libmono.so" to you Android ndk objdump tool folder(e.g.: "Android_NDK/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/windows/aarch64-linux-android/bin")
3. use terminal or cmd in Windows : objdump.exe -S libmono.so >> out.txt
4. check out.txt

2016年5月30日 星期一

Android pull down for list

Activity

package com.example.james_lue;

import android.app.Activity;
import android.content.Context;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;

public class MorePullDowTest extends Activity {
    private static final String LOG_TAG = MorePullDowTest.class.getSimpleName();
    MyListAdapter mListAdapter;
    MyScrollView myScrollView;
    ListView mListView;
    TextView mUpTextView;
    private int mUpHeight;
    private boolean isListTop = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_more_pull_dow_test);
        mListAdapter = new MyListAdapter(this);

        myScrollView = (MyScrollView) findViewById(R.id.main_scroll_view);
        myScrollView.setMorePullTest(this);
        mListView = (ListView)findViewById(R.id.main_list_view);

        mUpTextView = (TextView)findViewById(R.id.text_up);

        mListView.setAdapter(mListAdapter);
        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                    case SCROLL_STATE_IDLE:
                        View c = mListView.getChildAt(0);
                        int scrollY = -c.getTop() + mListView.getFirstVisiblePosition() * c.getHeight();
                        if (scrollY == 0) {
                            isListTop = true;
                        }

                        Log.d(LOG_TAG, "scroll: " + scrollState + ", top: " + scrollY);
                        break;
                    case SCROLL_STATE_TOUCH_SCROLL:
                    case SCROLL_STATE_FLING:
                        isListTop = false;
                        break;
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });

        myScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // Ensure you call it only once :
                myScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                // Here you can get the size :)
                ViewGroup.LayoutParams params = mListView.getLayoutParams();
                params.height = myScrollView.getHeight();
                params.width = myScrollView.getWidth();
                mUpHeight = mUpTextView.getHeight();
                mListView.setLayoutParams(params);
                myScrollView.requestLayout();
            }
        });

    }

    private static class MyListAdapter implements ListAdapter {

        private Context mContext;
        private MyListAdapter(Context context) {
            mContext = context;
        }
        @Override
        public boolean areAllItemsEnabled() {
            return true;
        }

        @Override
        public boolean isEnabled(int position) {
            return true;
        }

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public int getCount() {
            Log.d(LOG_TAG, "getCount");
            return 50;
        }

        @Override
        public Object getItem(int position) {
            return position + ": item";
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView textView;
            if(convertView == null) {
                textView = new TextView(mContext);
                textView.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
                textView.setHeight(100);
                textView.setTextSize(25.0f);

            } else {
                textView = (TextView)convertView;
            }
            textView.setText(position + ": item");
            //Log.d(LOG_TAG, "po: " + position);
            return textView;
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public int getViewTypeCount() {
            return 1;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }
    }

    public static class MyScrollView extends ScrollView{
        private float mInitialY;
        private MorePullDowTest mActivity;
        public MyScrollView(Context context) {
            super(context, null);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setFadingEdgeLength(0);
        }

        public void setMorePullTest(MorePullDowTest activity) {
            mActivity = activity;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mInitialY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float y = ev.getY();
                    float yD = y - mInitialY;

                    boolean shouldScrollBack = (yD > 0 && mActivity.isListTop);
                    Log.d(LOG_TAG, "yD: " + yD + "shouldScrollBack: " + shouldScrollBack + ", isTop: " + mActivity.isListTop);
                    if(shouldScrollBack) {
                        smoothScrollBy(0, 100 * (int) -yD);
                        mInitialY = y;
                        return true;
                    }
                    break;
            }
            Log.d(LOG_TAG, "intercept: " + ev.getActionMasked());
            return false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:

                    if (this.getScrollY() <= mActivity.mUpHeight) {
                        smoothScrollTo(0, mActivity.mUpHeight);
                        //mActivity.canScrollChild = true;
                    }

            }
            Log.d(LOG_TAG, "touch: " + event.getActionMasked());
            return super.onTouchEvent(event);
        }
    }
}


layout


    

        
            
            

2016年5月25日 星期三

Android View tricks

expand child of ScrollView to match ScorllView height:
adding android:fillViewport="true"to your ScrollView
link

2016年5月3日 星期二

unity讓物件面對滑鼠方向(make 3D object face to mouse cursor)

有人問了這問題
就放個code


using UnityEngine;
using System.Collections;

public class TraceMouse : MonoBehaviour {
    private Camera mainCamera;
 // Use this for initialization
 void Start () {
        mainCamera = Camera.main;
    }
 
 // Update is called once per frame
 void Update () {
        //transform object to screen space
        Vector3 transformScreenPosition = mainCamera.WorldToScreenPoint(transform.position);
        //use object's z in screen space as mouse's z in screen space, then calculate mouse's world position 
        Vector3 mouseWorldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transformScreenPosition.z));
        //direction from object to mouse
        Vector3 dir = (mouseWorldPosition - transform.position).normalized;
        //set direction to object's local axis
        transform.up = dir;
    }
}

2016年4月26日 星期二

Mosaic Shader for Unity

one of my friends wanted to make 3D h-game and he wondered how to make mosaic effect in 3D, so I did some research and made this



references:
风宇冲Unity3D教程学院 第二十三讲GrabPass
Dr.Dobb's Understanding Photomosaics
Shader "Custom/MosaicShader" {
 Properties{
  _MainTex("Base (RGB)", 2D) = "white" {}
 }

 SubShader
 {
  Tags{ "Queue" = "Transparent" }

  GrabPass
  {
   "_MyGrabTexture"
  }

  pass
  {
   Name "pass2"
   CGPROGRAM
   #pragma vertex vert
   #pragma fragment frag

   #include "UnityCG.cginc"
   sampler2D _MyGrabTexture;
   float4 _MyGrabTexture_ST;

   struct VertexIn
   {
    float4 vertex : POSITION;
    float2 texcoord : TEXCOORD0;
    float4 screenPos : TEXCOORD1;
   };
   struct VtoF 
   {
    float4  pos : SV_POSITION;
    float2  uv : TEXCOORD0;
    float4 screenPos : TEXCOORD1;
   };

   VtoF vert(VertexIn v)
   {
    VtoF o;
    o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord,_MyGrabTexture);
    /*
    //source code in UnityCG.cginc
    inline float4 ComputeScreenPos(float4 pos) {
     //if we do perspective divide here(pos/pos.w), we got pos.x in range (-1, 1)(NDC)
     float4 o = pos * 0.5f;
     //if we do perspective divide here(o/pos.w), we got o.x in range (-0.5,0.5)
     #if defined(UNITY_HALF_TEXEL_OFFSET)
     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;
     #else
     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
     #endif

     o.zw = pos.zw;
     //if we do perspective divide here(o/pos.w), we got o.x in range (0,1)
     //o.x / pos.w in range(-0.5, 0.5), o.w / pos.w = 0.5
     //(o.x + o.w) / pos.w in range (-0.5 + 0.5, 0.5 + 0.5) = (0,1)
     return o;
    }
    */
    o.screenPos = ComputeScreenPos(o.pos);
    return o;
   }
   float4 frag(VtoF i) : COLOR
   {
    //perspective divide for screen pos to normalize p to be in the range of (0,1)
    fixed4 p = i.screenPos / i.screenPos.w;
    float4 tmp = float4(0, 0, 0, 0);

    //http://docs.unity3d.com/462/Documentation/Manual/SL-BuiltinValues.html
    //_ScreenParams: screen dimension in pixel
    //p.x*_ScreenParams.x: get x coordinate on screen, unit: pixel
    //floor(p.x*_ScreenParams.x / 10) * 10: 100 -> 100, 101 -> 100, 102 -> 100 ..., 109 -> 100, 110 -> 110
    [unroll(10)]
    for (float ii = floor(p.x*_ScreenParams.x / 10) * 10; ii < floor(p.x*_ScreenParams.x / 10) * 10 + 10; ii += 1)
    {
     [unroll(10)]
     for (float jj = floor(p.y *_ScreenParams.y / 10) * 10; jj < floor(p.y *_ScreenParams.x / 10) * 10 + 10; jj += 1)
     {
      //sample pixel color on screen
      tmp += tex2D(_MyGrabTexture, float2(ii / _ScreenParams.x, 1 - jj / _ScreenParams.y));
     }
    }
    return tmp / 100;//average samples
   }
   ENDCG
  }
 }
}

2016年4月21日 星期四

Android recycler view inflation error

stack overflow
I meet this problem today. And solved it.

first step:keep the support-libs you used are same version

compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'


second step:you should add recyclerView to your proguard files

-keep class android.support.v7.widget.** {*;}
// I`ve just keep all widgets

2016年4月18日 星期一

NDC to world coordinates

here is the original link
link

We transform it to clip space by multiplying it with our projection/modelview matrix.

clip = Matrix\text{ }world
Then move on to device coordinates by dividing with w.

device = clip_{xyz} / clip_w
So the problem we face is: given clip = Matrix\text{ }worlddevice = clip_{xyz} / clip_wworld_w = 1,
and given device as an input and Matrix as a constant, calculate world.
Let’s walk through it. Invert the first step:

Matrix^{-1}\text{ }clip = Matrix^{-1}\text{ }Matrix\text{ }world

Matrix^{-1}\text{ }clip = world
Now let’s see what we can do with the second equation.

device = clip_{xyz} / clip_w

clip_w\text{ }device = clip_{xyz}
Let’s use this syntax to indicate a 4-vector formed by combining a 3-vector and a fourth number:

clip = clip_{xyzw} = (clip_{xyz}, clip_w)
substitute clip_{xyz}

clip = (clip_w\text{ }device, clip_w)
insert into our earlier equation

Matrix^{-1}\text{ }clip = world

Matrix^{-1}\text{ }(clip_w\text{ }device, clip_w) = world

Matrix^{-1}\text{ }clip_w\text{ }(device, 1) = world
And note that since matrices are linear transforms, we can pull that clip_w in front of the matrix multiply:

clip_w\text{ }Matrix^{-1}\text{ }(device, 1) = world
So it seems we run into a wall. clip_w is lost, right? Don’t give up hope: we haven’t used the third of our initial givens yet.

world_w = 1
So let’s look at just the w component of that last equation there:

clip_w\text{ }\left(Matrix^{-1}\text{ }(device, 1)\right)_w = world_w = 1
Divide:

clip_w = \frac 1 {\left(Matrix^{-1}\text{ }(device, 1)\right)_w}
And insert into the equation that previously gave us trouble:

\frac{Matrix^{-1}\text{ }(device, 1)}{\left(Matrix^{-1}\text{ }(device, 1)\right)_w} = world
Or in other words:

\left(Matrix^{-1}\text{ }(device, 1)\right)_{xyz/w} = world\text{ ... and done.}