article search result of 'unity3d' : 27

  1. 2015.10.03 유니티에서 Affine Fix 변환 ( Perspective Correction Transform)
  2. 2015.10.02 윈도우8 에서 VSCode 사용하기 (3)
  3. 2015.07.23 서비스 호출시 HttpWebRequest, HttpWebResponse 에 대한 인증 오류 해결 방법
  4. 2015.04.23 CommandBuffer 를 이용한 Multipass Shader 기법
  5. 2014.09.10 올바른 Bitmap To Texture2D (4)
  6. 2014.09.10 로드된 텍스쳐 메모리 관리
  7. 2014.06.18 System.Drawing.Graphics able in the Unity3d (4.5.3) (30)
  8. 2014.04.05 Unity3d 에서 Unsafe 코드를 사용하는 방법
  9. 2014.04.04 Unity3d 의 Textrue2D 의 데이터를 Marshal.copy 를 이용하여 로딩 하기
  10. 2014.03.10 프로젝트의 파일을 빌드시 Export 하기
  11. 2014.02.21 [Shader] Circle Transition + MousePosition
  12. 2014.02.21 [Shader] Circle Transition
  13. 2014.02.20 [Shader]TriBlend + Circle
  14. 2014.02.20 [메모] 유니티 Cg Shader 참고
  15. 2013.12.06 [메모]Unity3d 다른 GameObject 의 Script Component 접근하기 (1)
  16. 2013.10.05 Matrix4x4를 이용한 회전, 스케일 중심점 설정하기 (3)
  17. 2013.10.04 PixelPerfect 설정하기
  18. 2013.08.06 [Unity3D] c# 별도의 클래스를 인스펙터와 연결 [System.Serializable]
  19. 2013.08.01 Unity3d 에 OpenGL 사용하기
  20. 2013.08.01 유니티의 친절한 Ray cast 사용법 (3)
  21. 2013.07.31 두개의 카메라를 한 화면에 렌더링 (3)
  22. 2013.07.29 웹캠의 활용, 캡처 뜨기
  23. 2013.07.29 Shader 의 적용부터 프로퍼티 접근까지 (2)
  24. 2013.07.17 OGV 변환 하기 (3)
  25. 2013.04.21 Custom Plane (3)
  26. 2013.04.20 unity3d 의 다이나믹 텍스트 렌더링 방법 (1)
  27. 2013.04.20 두개의 텍스쳐를 자연그럽게 교체하기

유니티에서 Affine Fix 변환 ( Perspective Correction Transform)

정점 4개 QUAD의 정점들을 직 사각형  형태에서 벗어나 2차원 좌표를 움직여 마름모를 표현 할때 텍스쳐의 UV 맵 방식의 변화가 필요하다.


아래와 같은 문제가 발생 한다.



출처 : http://i.stack.imgur.com/8JS1D.jpg





이것을 해결한 Package 를 업로드 하였다.


AffineFix.unitypackage


본 출처는 http://forum.unity3d.com/threads/correcting-affine-texture-mapping-for-trapezoids.151283/ 여기 이고 Unity5 에 맞도록 쉐이더를 수정 하였다.

Yamecoder 야매코더_
unity3d 2015.10.03 11:09

윈도우8 에서 VSCode 사용하기


이미 플러그인 VSCode 를 사용하기 위한 플러그인이 있다.


https://github.com/dotBunny/VSCode


이것을 다운 받고 간단히 유니티의 Asset 창으로 밀어 넣자. 


그리고 pdf 에 나온데로 Preference 에서 설정을 해주면 된다.



잘 적용이 되었다면 불필요한 파일들(특히 *.meta) 는 VSCode 에서 보이지 않는다.


하지만 인텔리 센스가 깨지는 문제가 발생 할것이다.





이유는 바로 .net framework 의 버전을 못찾아서 이다. 기본적으로 UnityEngine.dll 의 .Net 버전은 3.5 이다. 하지만 Assembly-CSharp.csproj 를 분석해 보면 




라고 당당하게 쓰여져 있다. 그리고 이것을 v3.5 로 고치면 레퍼런스가 완성 되지만 유니티프로젝트를 새로 고침 하거나 하면 다시 원복 된다. 

플러그인 코드를 고칠 필요가 있다.


앞서 다운로드한 플러그인 폴더 안의 VSCode.cs 를 찾아서 열자.



위의 478번 줄 의 if 문에 의하여 자꾸 바뀌는 것 인데, 이것을 모두 주석 처리 했다. 


그러니 원하는대로 v3.5 가 유지 되었다.




덧붙여서 본인은 VS Debug 기능을 하용하지 않는다. 때문에 

UnityEngine.Debug.Log("[VSCode] Updating Solution & Project Files");


코드들을 모두 찾아서 주석 처리 했다.




위의 수정사항들을 적용한 프로젝트를 첨부파일에 업로드 하였음.


Plugins.zip



Yamecoder 야매코더_
unity3d 2015.10.02 15:40

서비스 호출시 HttpWebRequest, HttpWebResponse 에 대한 인증 오류 해결 방법


인스타그램 Open API 를 이용하기 위하여 일반적인 방법으로 HttpWebRequest, HttpWebResponse 를 이용하였지만 아래와 같은 에러가 났다.



에러내용 

System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a

  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0 

  at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1 () [0x00000] in <filename unknown>:0 

  at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process () [0x00000] in <filename unknown>:0 

  at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process ()

  at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0 

  at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 

  --- End of inner exception stack trace ---

  at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 

  --- End of inner exception stack trace ---

  at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 

  at System.Net.HttpWebRequest.GetResponse () [0x00000] in <filename unknown>:0 

  at InstagramManager+<SingleRunToSearchTag>c__AnonStorey5.<>m__0 () [0x00091] in 





해결방법은 간단히 아래의 명령을 시작시 해주면 된다.


ServicePointManager.ServerCertificateValidationCallback = (p1, p2, p3, p4) => true;

Yamecoder 야매코더_
unity3d 2015.07.23 15:07

CommandBuffer 를 이용한 Multipass Shader 기법





기본적으로 쉐이딩 프로그래밍은 1점에 대하여 컬러를 연산 하는것 이기 때문에, Blur 등과 같은 효과를 최종적으로 주기 위하서는 앞서 쉐이더 처리된 완성된 비트맵이 필요 하다. 하지만 안타깝게도 유니티에서 이러한 처리를 하기에는 별도의 카메라의 렌더택스쳐가 필요하거나, FPS 저하의 1등 공신 Texture2D.Apply() 을 써야 하는 상황이 벌어진다.


하지만 멀티 패스 를 이용하여 처리를 할 수 있다면, 쉐이더 처리결과를 비트맵으로 옮기고 다시 읽는 불필요한 과정이 없다면 가능하지 않을까 해서 조사를 시작, 

다행이도 이번 Unity3d 5 의 새로운 API 인 CommandBuffer 를 이용한다면 가능하다.



기본적인 아이디어는 아래와 같다.








커맨드 버퍼를 이용한다면 같은 Pass 를 여러번 처리하는것도 가능하여 특히 Blur 효과에 탁월 할 것이다. 
아래는 실제로 위의 아이디어를 이용한 예제 이다.




간단한 기능의 멀티패스 쉐이더 (MultipassSample.shader)

Shader "Custom/MultipassSample" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "" {}
    }

    
    
Subshader {

    //Pass 1 : 텍스처 컬러 반전
    pass  
    {  
        CGPROGRAM  
        
        #pragma fragment frag  
        #pragma vertex vert_img  
        #include "UnityCG.cginc"  
         
        sampler2D _BgTex;
        float4 frag(v2f_img input) : COLOR  
        {  
            float4 color = tex2D(_BgTex , input.uv);
            color = 1 - color;
            color.a = 1;
            return color;
        }  
        ENDCG  
    }  
    
    //Pass 2 : 블러 이펙트
    Pass {
        ZTest Always Cull Off ZWrite Off
        Fog { Mode off }

        CGPROGRAM
        #pragma fragmentoption ARB_precision_hint_fastest
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct v2f {
            float4 pos : POSITION;
            float2 uv : TEXCOORD0;
            
            float4 uv01 : TEXCOORD1;
            float4 uv23 : TEXCOORD2;
            float4 uv45 : TEXCOORD3;
        };
        
        float4 offsets;

        sampler2D _MainTex;

        v2f vert (appdata_img v) {
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

        o.uv.xy = v.texcoord.xy;

        o.uv01 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1);
        o.uv23 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;
        o.uv45 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;

        return o;
        }
        
        half4 frag (v2f i) : COLOR {
            half4 color = float4 (0,0,0,0);

            color += 0.40 * tex2D (_MainTex, i.uv);
            color += 0.15 * tex2D (_MainTex, i.uv01.xy);
            color += 0.15 * tex2D (_MainTex, i.uv01.zw);
            color += 0.10 * tex2D (_MainTex, i.uv23.xy);
            color += 0.10 * tex2D (_MainTex, i.uv23.zw);
            color += 0.05 * tex2D (_MainTex, i.uv45.xy);
            color += 0.05 * tex2D (_MainTex, i.uv45.zw);

            return color;
        }
        ENDCG
    }
}

Fallback off

} // shader

dsadsadsad





(매우 간단한) 최종적 으로 렌더링할 쉐이더 (Final.shader)



Shader "Custom/Final" {
SubShader 
{
    Pass 
    {   
        CGPROGRAM
        #pragma vertex vert_img
        #pragma fragment frag
        #include "UnityCG.cginc"

        sampler2D _FinalTexture;
        
        half4 frag (v2f_img input) : SV_Target
        {
            return tex2D(_FinalTexture , input.uv);
        }
        ENDCG
    }
}
}


aaa





오브젝트에 사용할 C# 코드


using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;


public class BlurTest1 : MonoBehaviour {

    public Shader MultiPassShader;
    public Texture2D _test_screen_texure;

    private Material _blurMaterial;
    private Camera _cam;

    private float blurAmount = 1;
    private Texture2D _main_texure;
    private bool _isReady = false;

    // Use this for initialization
    void Start () {
    }
    
    void CleanUp()
    {
        _isReady = false;
        Object.DestroyImmediate(_blurMaterial);
    }

    
    //OnWillRenderObject 에 넣어야만 동작 한다
    //http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnWillRenderObject.html
    void OnWillRenderObject()
    {


        //--------------------------------------------------------------------------반복적인 처리가 되지 않도록 함.
        #region
        

        if ((gameObject.activeInHierarchy && enabled) == false)
        {
            CleanUp();
            return;
        }

        _cam = Camera.current;
        if (_cam == null)
        {
            _isReady = false;
            return;
        }

        if (_isReady == true) return;

        #endregion
    






        if (_blurMaterial == null)
        {
            _blurMaterial = new Material(MultiPassShader);
            _blurMaterial.SetTexture("_BgTex", _test_screen_texure);
            _blurMaterial.hideFlags = HideFlags.HideAndDontSave;
        }

        //빈 텍스쳐를 생성하여 텍스쳐 버퍼를 채워준다.
        if (_main_texure != null)
        {
            _main_texure = new Texture2D(1024, 1024);
        }




        //커맨드 버퍼 생성
        CommandBuffer buf = new CommandBuffer();




        //--------------------------------------------------------------------------첫번째 Pass
        #region


        //CommandBuffer의 GetTemporaryRT 를 이용하여 임시 공간을 확보 한다.
        //http://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.GetTemporaryRT.html
        int screenCopyID = Shader.PropertyToID("_ScreenCpyTexture");
        buf.GetTemporaryRT(screenCopyID, -1, -1, 0, FilterMode.Bilinear);

        //BuiltinRenderTextureType.CameraTarget 으로 현재 카메라가 바라보고 있는 화면을 텍스쳐로 가져 올 수 있다
        //기존의 [GrabPass{}] 와 유사하다.
        //buf.Blit(BuiltinRenderTextureType.CameraTarget, screenCopyID);



        //_main_texure 의 데이터를 screenCopyID 에 할당된 공간으로 _blurMaterial 의 0번 Pass 를 통하여 저장한다.
        buf.Blit(_main_texure, screenCopyID, _blurMaterial, 0);


        #endregion








        //--------------------------------------------------------------------------두번째 Pass
        #region


        // 2개의 GetTemporaryRT 를 선언 한것은 , 연산 결과를 서로 주고 받아 블러 효과를 극대화 하기 위함이다.
        int blurredID1 = Shader.PropertyToID("_Tmp1");
        buf.GetTemporaryRT(blurredID1, -2, -2, 0, FilterMode.Bilinear);

        int blurredID2 = Shader.PropertyToID("_Tmp2");
        buf.GetTemporaryRT(blurredID2, -2, -2, 0, FilterMode.Bilinear);


        //우선 blurredID1 에 첫번째 Pass 의 결과 데이터를 옮긴다.
        buf.Blit(screenCopyID, blurredID1);


        //더이상 사용되지 않음으로 제거한다.
        buf.ReleaseTemporaryRT(screenCopyID);



        //Blur 효과 , blurredID1 과 blurredID2 가 가로, 세로를 반복적으로 주고 받아 풍성한 블러 효과를 만든다.
        if (true)
        {
            //// 가로 흐림
            buf.SetGlobalVector("offsets", new Vector4(2.0f * blurAmount / Screen.width, 0, 0, 0));
            buf.Blit(blurredID1, blurredID2, _blurMaterial, 1);
            // 세로 흐림
            buf.SetGlobalVector("offsets", new Vector4(0, 2.0f * blurAmount / Screen.height, 0, 0));
            buf.Blit(blurredID2, blurredID1, _blurMaterial, 1);
            //// 가로 흐림
            buf.SetGlobalVector("offsets", new Vector4(4.0f * blurAmount / Screen.width, 0, 0, 0));
            buf.Blit(blurredID1, blurredID2, _blurMaterial, 1);
            //// 세로 흐림
            buf.SetGlobalVector("offsets", new Vector4(0, 4.0f * blurAmount / Screen.height, 0, 0));
            buf.Blit(blurredID2, blurredID1, _blurMaterial, 1);
        }

        //최종적으로 계산된 버퍼를 현재 GameObject 의 Material Shader 의 _FinalTexture 에 넣어준다.
        buf.SetGlobalTexture("_FinalTexture", blurredID1);

        #endregion


        //buf 를 현재 카메라 렌더링에 반영한다. 
        //(순서를 정할수 있다. 하지만 아직 렌더링 순서에 대하여 명확하게 이해하지 못했다.)
        _cam.AddCommandBuffer(CameraEvent.AfterSkybox, buf);


        Debug.Log("Process complete");
        buf.Dispose();
        _isReady = true;
    }
}

ㅁㅁㅁ

유니티 프로젝트 

결과는 아래와 같다.




비로서 Pass 간의 결과를 주고 받을 수 있게 되었다.



Yamecoder 야매코더_
unity3d 2015.04.23 20:42

올바른 Bitmap To Texture2D





L사 프로젝트를 하면서 사용자의 옷을 실시간으로 분석하여 컬러를 뽑아 그것을 캐릭터화 하는 작업이 있었는데, GdI의  Bitmap 과 유니티의  Texture2D 의 색상을 완벽히 변경 하지 못하여 억지로 로컬에 저장 하였다가 그것을 WWW 로 불러와 넣었었다. 한번에 할 수 있는것을 엄청 번거러운 작업 이었다.


그때 왜 그랬나 이유는 잘 모르겠지만 그 상황을 재현 한 결과 아래와 같이 기술 하면 된다. 유의 할 점은 GDI 의 Format32bppArgb 와 Texture2D 의 ARGB32 는 엄현히 다르다. 그래서 그것을 그대로 넣어주었다가는 언듯 색상은 비슷하나 ,  RGB 원색이 정확히 표현 되지 않아 정확한 색상 전환이 불가하다.


\




using UnityEngine;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using System;
using System.Runtime.InteropServices;
using XGraphics = System.Drawing.Graphics;

public class TextureColorTest : MonoBehaviour {

    private static string TestFileName = "testCloth.png";
    private Bitmap canvas;
    private Texture2D textureCanvas;
    private bool IsReady = false;

    public Texture2D TestTexture = null;
    public Material TargetMat;

    void Start () {
        canvas = new Bitmap(TestFileName);
        CreateTexture();
    }

    void OnGUI()
    {
        if (IsReady == true)
        {
            GUI.DrawTexture(new Rect(0,0,256, 256), textureCanvas);
        }
    }


    /// <summary>
    /// 새로운 비트맵(32bit PNG)을 불러와 그 위에 색상테스트를 위하여 R , G , B 의 3색 사각형 을 새롭게 그린다음
    /// byte 배열로 전환하여 Texture2D 에 넣는 과정
    /// </summary>
    private void CreateTexture()
    {
        using (XGraphics g = XGraphics.FromImage(canvas))
        {
            g.FillRectangle(Brushes.Red, new Rectangle(50, 50, 10, 10));
            g.FillRectangle(Brushes.Green, new Rectangle(60, 50, 10, 10));
            g.FillRectangle(Brushes.Blue, new Rectangle(70, 50, 10, 10));
        }
        canvas.Save(@"test2.jpg");
        var data = BitmapToByteUseMarshal(canvas);

        textureCanvas = new Texture2D(canvas.Width, canvas.Height, TextureFormat.ARGB32, false);
        textureCanvas.LoadRawTextureData(data);
        textureCanvas.Apply();

        TargetMat.mainTexture = textureCanvas;

        IsReady = true;
    }



    /// <summary>
    /// 완성된 비트맵을 Texture2D 로 넣기 위하여 byte 배열로 변환함
    /// </summary>
    public byte[] BitmapToByteUseMarshal(Bitmap bitmap)
    {
        BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        Debug.Log("format : " + bitmap.PixelFormat);

        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes]; 
        byte[] translateBGRAData = new byte[numbytes];

        IntPtr ptr = bmpdata.Scan0;
        Marshal.Copy(ptr, bytedata, 0, numbytes);

        //변환시 배열 순서 유의
        for (int i = 0; i < bytedata.Length; i += 4)
        {
            translateBGRAData[i + 3] = (byte)(bytedata[i + 0]);

            translateBGRAData[i + 2] = (byte)(bytedata[i + 1]);

            translateBGRAData[i + 1] = (byte)(bytedata[i + 2]);

            translateBGRAData[i + 0] = (byte)(bytedata[i + 3]);// 255
        }

        bitmap.UnlockBits(bmpdata);
        return translateBGRAData;
    }
}

Yamecoder 야매코더_
unity3d 2014.09.10 17:31

로드된 텍스쳐 메모리 관리


MemoryTest.zip










동적으로 로드된 Texture2D 는 소거 할때 잘 지워 줘야 메모리에 쌓이지 않는다. 


실제로 1024 x 1024 텍스쳐 하다당 약 6 메가에 육박하는 메모리가 올라가기 때문이다. 


테스트는 임의의 텍스쳐(1024 x 1024)를 만들고 이것을 여러가지 방법으로 적용 한 다음 소멸 시켜 보는것이다.







Test1 : 단순 Texture2D 를 로드 하여 List<Texture2D> 에 담고 이것을 한방에 지움 

 => 결과 : 잘 소거됨


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MemoryTest1 : MonoBehaviour {

    private static string TestFileName = "test.jpg";
    public Texture2D TestTexture = null;
    public List<Texture2D> txs = new List<Texture2D>();

    void Start () {

        System.IO.File.WriteAllBytes( TestFileName , TestTexture.EncodeToJPG());

    }

    void OnGUI()
    {
        GUI.Label(new Rect(0,0,Screen.width , 30) ,"loaded texture  : " + txs.Count);

        if (GUI.Button(new Rect(0, 30, Screen.width / 4, 30), "Create"))
        {
            CreateTexture();
        }
        if (GUI.Button(new Rect(0, 60, Screen.width / 4, 30), "Delete All"))
        {
            DeleteTexture();
        }
    }

    void Update () {
    }

    private void CreateTexture()
    {
        StartCoroutine( LoadTexture());
    }

    WWW loader;
    IEnumerator LoadTexture()
    {
        loader = new WWW(@"file://" + TestFileName);
        yield return loader;
        txs.Add(loader.texture);
        Debug.Log("Texture Load");
    }


    /// <summary>
    /// 완벽 소거
    /// </summary>
    private void DeleteTexture()
    {
        int len = txs.Count;
        for (int i = 0; i < len; i++)
        {
            Texture2D.DestroyImmediate(txs[i]);
        }
        txs.Clear();
    }
}









Test2 : 큐브를 만들고 그것을 복제 하여 로드된 텍스쳐를 씌운다음 텍스쳐를 한방에 제거함. 

 => 결과 : 일부만 제거됨. 



using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MemoryTest2 : MonoBehaviour {

    private static string TestFileName = "test.jpg";

    public GameObject Parent;
    public GameObject TargetObject;
    public Texture2D TestTexture = null;

    private List<Texture2D> txs = new List<Texture2D>();
    // Use this for initialization
    void Start () {
        System.IO.File.WriteAllBytes(TestFileName, TestTexture.EncodeToJPG());
    }
    
    // Update is called once per frame
    void Update () {
        

        Parent.transform.Rotate(1, 1, 1);
    }

    void OnGUI()
    {
        GUI.Label(new Rect(0,0,Screen.width , 30) ,"loaded texture  : " + txs.Count);
        if (GUI.Button(new Rect(0, 30, Screen.width / 4, 30), "Create"))
        {
            CreateTexture();
        }
        if (GUI.Button(new Rect(0, 60, Screen.width / 4, 30), "Delete All"))
        {
            DeleteTexture();
        }
    }

    private void CreateTexture()
    {
        GameObject cloneCube = (GameObject)GameObject.Instantiate(TargetObject, new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), Random.Range(-3f, 3f)), Quaternion.identity);
        cloneCube.transform.parent = Parent.transform;
        StartCoroutine(LoadTexture(cloneCube));
    }

    WWW loader;
    IEnumerator LoadTexture(GameObject target)
    {
        loader = new WWW(@"file://" + TestFileName);
        yield return loader;
        target.renderer.material.mainTexture = loader.texture;
        txs.Add(loader.texture);
    }


    /// <summary>
    /// 일부 소거 됨 그러나 화면상에는 지워지지 않았음.
    /// </summary>
    private void DeleteTexture()
    {
        int len = txs.Count;
        for (int i = 0; i < len; i++)
        {
            Texture2D.DestroyImmediate(txs[i]);
        }
        txs.Clear();
    }
}






Test3 : 큐브를 만들고 텍스쳐를 씌운다음 오브젝트만 지움 

=> 결과 : 전혀 소거 되지 않음



using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MemoryTest3 : MonoBehaviour
{

    private static string TestFileName = "test.jpg";

    public GameObject Parent;
    public GameObject TargetObject;
    public Texture2D TestTexture = null;

    //private List<Texture2D> txs = new List<Texture2D>();
    private List<GameObject> objs = new List<GameObject>();
    // Use this for initialization
    void Start()
    {
        System.IO.File.WriteAllBytes(TestFileName, TestTexture.EncodeToJPG());
    }

    // Update is called once per frame
    void Update()
    {
        Parent.transform.Rotate(1, 1, 1);
    }

    void OnGUI()
    {
        GUI.Label(new Rect(0, 0, Screen.width, 30), "loaded texture  : " + objs.Count);

        if (GUI.Button(new Rect(0, 30, Screen.width / 4, 30), "Create"))
        {
            CreateTexture();
        }
        if (GUI.Button(new Rect(0, 60, Screen.width / 4, 30), "Delete All"))
        {
            DeleteTexture();
        }
    }

    private void CreateTexture()
    {
        GameObject cloneCube = (GameObject)GameObject.Instantiate(TargetObject, new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), Random.Range(-3f, 3f)), Quaternion.identity);
        cloneCube.transform.parent = Parent.transform;
        StartCoroutine(LoadTexture(cloneCube));
    }

    WWW loader;
    IEnumerator LoadTexture(GameObject target)
    {
        loader = new WWW(@"file://" + TestFileName);
        yield return loader;
        target.renderer.material.mainTexture = loader.texture;
        objs.Add(target);
    }


    /// <summary>
    /// 전혀 소거 되지 않음.
    /// </summary>
    private void DeleteTexture()
    {
        int len = objs.Count;
        for (int i = 0; i < len; i++)
        {
            GameObject.DestroyImmediate(objs[i]);
        }
        objs.Clear();
        objs = new List<GameObject>();
    }
}



Test4 : 큐브를 만들고 텍스쳐와 오브젝트를 지움.

=> 결과 : 잘 소거됨



using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MemoryTest4 : MonoBehaviour
{
    private static string TestFileName = "test.jpg";

    public GameObject Parent;
    public GameObject TargetObject;
    public Texture2D TestTexture = null;

    private List<GameObject> objs = new List<GameObject>();
    void Start()
    {
        System.IO.File.WriteAllBytes(TestFileName, TestTexture.EncodeToJPG());
    }

    void Update()
    {
        Parent.transform.Rotate(1, 1, 1);
    }

    void OnGUI()
    {
        GUI.Label(new Rect(0, 0, Screen.width, 30), "loaded texture  : " + objs.Count);

        if (GUI.Button(new Rect(0, 30, Screen.width / 4, 30), "Create"))
        {
            CreateTexture();
        }
        if (GUI.Button(new Rect(0, 60, Screen.width / 4, 30), "Delete All"))
        {
            DeleteTexture();
        }
    }

    private void CreateTexture()
    {
        GameObject cloneCube = (GameObject)GameObject.Instantiate(TargetObject, new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), Random.Range(-3f, 3f)), Quaternion.identity);
        cloneCube.transform.parent = Parent.transform;
        StartCoroutine(LoadTexture(cloneCube));
    }

    WWW loader;
    IEnumerator LoadTexture(GameObject target)
    {
        loader = new WWW(@"file://" + TestFileName);
        yield return loader;
        target.renderer.material.mainTexture = loader.texture;
        objs.Add(target);
    }


    /// <summary>
    /// 잘 소거 됨
    /// </summary>
    private void DeleteTexture()
    {
        int len = objs.Count;
        for (int i = 0; i < len; i++)
        {
            Texture2D.DestroyImmediate((Texture2D)objs[i].renderer.material.mainTexture);
            GameObject.DestroyImmediate(objs[i]);
        }
        objs.Clear();
        objs = new List<GameObject>();
    }
}

ㅁㅁ


Test5 : Test4의 상황에서 텍스쳐 리스트를 만들고 명시적으로 지워주지 않았음 
=> 결과 : 일부만 소거됨 , 만약 DeleteTexture 의 주석을 제거 한다면 완벽 제거됨


using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MemoryTest5 : MonoBehaviour
{

    private static string TestFileName = "test.jpg";

    public GameObject Parent;
    public GameObject TargetObject;
    public Texture2D TestTexture = null;

    private List<Texture2D> txs = new List<Texture2D>();
    private List<GameObject> objs = new List<GameObject>();
    void Start()
    {
        System.IO.File.WriteAllBytes(TestFileName, TestTexture.EncodeToJPG());
    }

    void Update()
    {
        Parent.transform.Rotate(1, 1, 1);
    }

    void OnGUI()
    {
        GUI.Label(new Rect(0, 0, Screen.width, 30), "loaded texture  : " + objs.Count);

        if (GUI.Button(new Rect(0, 30, Screen.width / 4, 30), "Create"))
        {
            CreateTexture();
        }
        if (GUI.Button(new Rect(0, 60, Screen.width / 4, 30), "Delete All"))
        {
            DeleteTexture();
        }
    }

    private void CreateTexture()
    {
        GameObject cloneCube = (GameObject)GameObject.Instantiate(TargetObject, new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), Random.Range(-3f, 3f)), Quaternion.identity);
        cloneCube.transform.parent = Parent.transform;
        StartCoroutine(LoadTexture(cloneCube));
    }

    WWW loader;
    IEnumerator LoadTexture(GameObject target)
    {
        loader = new WWW(@"file://" + TestFileName);
        yield return loader;
        target.renderer.material.mainTexture = loader.texture;
        objs.Add(target);
        txs.Add(loader.texture);
    }


    /// <summary>
    /// 일부만 소거됨
    /// </summary>
    private void DeleteTexture()
    {
        int len = objs.Count;
        for (int i = 0; i < len; i++)
        {
            Texture2D.DestroyImmediate((Texture2D)objs[i].renderer.material.mainTexture);
            GameObject.DestroyImmediate(objs[i]);

        }
        objs.Clear();
        objs = new List<GameObject>();

        //for (int i = 0; i < txs.Count; i++)
        //{
        //    Texture2D.DestroyImmediate(txs[i]);
        //}
        txs.Clear();
        txs = new List<Texture2D>();
    }
}


Yamecoder 야매코더_
unity3d 2014.09.10 16:09

System.Drawing.Graphics able in the Unity3d (4.5.3)

유니티 버전 4.5.3 에서 System Bitmap 생성이 가능하고 , 또한 GDI 의 Graphic 사용이 가능하다.

(이전버전에서는 유니티 자체가 에러 나며 죽었다.)



물론 BGR => RGB 변환 을 수동으로 해야겠지만 , 


하지만 역시 Texture->Apply() 과정에서 퍼포먼스 저하가 상당하다.

using UnityEngine;
using System.Collections;
using System.Drawing;
using XGraphic = System.Drawing.Graphics;
using XFont = System.Drawing.Font;
using System.IO;
using System.Drawing.Imaging;
using System;
using System.Runtime.InteropServices;

public class TestMain : MonoBehaviour {

    // Use this for initialization

    Bitmap bmp = new Bitmap(960, 540);
    Texture2D _tex;
    void Start () {
        var data = BitmapToByteUseMarshal(bmp);

        Texture2D tex = new Texture2D(bmp.Width, bmp.Height, TextureFormat.ARGB32, false);
        tex.LoadRawTextureData(data);
        tex.Apply();

        Debug.Log(bmp.PixelFormat); // ARGB32
        _tex = tex;
    }

    // Bitmap to Byte from Marshal


    private readonly XFont degreeFont = new XFont("Consolas", 10);
    XFont blobLabelfont = new XFont("Arial", 20);
    XFont matchFont = new XFont("Arial", 15);
    XFont RateFont = new XFont("Arial", 30);

    private readonly Brush centerPointBrush = Brushes.Blue;
    private readonly Brush inferredJointBrush = Brushes.Yellow;
    private readonly Pen trackedBonePen = new Pen(Brushes.Green, 10);
    private readonly Pen inferredBonePen = new Pen(Brushes.Black, 10);


    public static byte[] BitmapToByteUseMarshal(Bitmap bitmap)
    {
        BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes];
        IntPtr ptr = bmpdata.Scan0;

        Marshal.Copy(ptr, bytedata, 0, numbytes);

        bitmap.UnlockBits(bmpdata);

        return bytedata;
    }

    //됨!
    void OnGUI()
    {
        if (_tex != null)
        {
            c += 0.1f;
            float x = Mathf.Sin(c) * 10;

            //Draw System Graphics
            var g = XGraphic.FromImage(bmp);
            g.Clear(System.Drawing.Color.White);
            g.DrawRectangle(Pens.Red, Mathf.Abs(x), 0, 300, 300);

            g.DrawString("A", RateFont , Brushes.Blue , new PointF(0,0));

            //render to Unity3d 
            var data = BitmapToByteUseMarshal(bmp);
            _tex.LoadRawTextureData(data);
            _tex.Apply();


            Matrix4x4 mat = GUI.matrix;

            Quaternion q = Quaternion.identity;
            q.z = 90;
            GUI.matrix = Matrix4x4.TRS(new Vector3(0,bmp.Height , 0), Quaternion.identity, new Vector3(1, -1, 1));
            GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), _tex);

            GUI.matrix = mat;
        }
    }

    // Update is called once per frame
    private float c = 0;
}



Yamecoder 야매코더_
unity3d 2014.06.18 18:33

Unity3d 에서 Unsafe 코드를 사용하는 방법


gmcs.rsp


smcs.rsp



위의 두 파일을 Assets 안에다가 넣기만 하면 된다.


안의 내용은 그저 


-unsafe 


이다. 


사실 저것들은 유니티 빌드 옵션 선언 파일 이다.


이제 c# 에서 


unsafe{

...

}


를 사용하여도 에러가 나지 않고, 더불어 Ptr 을 자유롭게 사용이 가능하다.

 



Yamecoder 야매코더_
unity3d 2014.04.05 00:21

Unity3d 의 Textrue2D 의 데이터를 Marshal.copy 를 이용하여 로딩 하기

1. 기조연설

아쉽게도 아직 유니티의 Texture2D는 메인스레드가 아닌 다른 스레드에서 접근을 허용하지 않기에 , 런타임중 다른 텍스쳐를 읽어 오거나 할때 그 이미지의 사이즈가 크면 약간의 덜컥거림이 있다. 이 문제는 그냥 내장된 Asset을 이용하여 게임을 만드는 분들에게는 그리 위중한 문제가 아닐수 있으나 Installation , Digital Signage 처럼 실시간으로 사진을 전송 받거나 할때, 다소 걸림돌이 되는건 사실이다.


그래서 이번에 시도한 방법은 백그라운드 스레드에서 Texture2D의 GetNativeTexturePtr() 을 이용한 직접적인 포인터로 byte[]를 밀어 넣는것이 었지만 안타깝게도 실패 하였다. 

증상은 별다는 에러 메시지는 뜨지 않고, 그냥 에디터가 죽는다. 당황스럽지만, 조심스레 예상하자면, 아마도 다른 엔진들과 달리 유니티의 Texture2D는 생성과 동시에 그래픽카드에 업로드 되고 그 쓰레드를 메인스레드가 물고 있기 때문에 다른 스레드에서 접근 한다면 정상적으로 에러를 내밷지만, 직접적인 포인터 접근시, 이를 처리하지 못하고 유니티 자체가 맹렬히 전사해 버리는 사태가 나온다고 예상한다. 


제발 다음 버전에 해결해 주기를... 


2. 테스트 내용 

앞서 말한것 처럼 완벽한 백그라운드스레드에서의 텍스쳐 로딩은 못하지만, 최대한 블록 현상을 줄여 보기로 했다. 그리고 발견한 

Texture2D 의 LoadRawTextureData(byte[]). 이것을 이용해보자. 


코드는 아래와 같다. (참고로 준비된 이미지는 1920x1080 png 이다.)

using UnityEngine;
using System.Collections;
using System.Drawing.Imaging;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;

public class Main_loadTest_1 : MonoBehaviour
{

    // Use this for initialization
    private GameObject TestCube;
    public Texture2D tex;
    private BackgroundWorker bw = new BackgroundWorker();

    private byte[] img_bytes;
    private Size img_size = new Size(1920, 1080);
    private bool isComplete = false;
    private int workcount = 1;

    void Start()
    {
        TestCube = GameObject.Find("Cube");
        tex = new Texture2D(img_size.Width, img_size.Height, TextureFormat.RGB24, false, false);

        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
    }




    /*
     * commands
     * */
    /// <summary>
    /// BackgoundThread 가 완료 되면 isComplete 을 true 로 바꾸는데, 이를 코루틴 + while 루프로 체크 하다가
    /// 완료가 되면 LoadRawTextureData 로 바이트를 넣는다.
    /// </summary>
    IEnumerator cothread()
    {
        while (isComplete == false)
        {
            yield return new WaitForEndOfFrame(); 
        }
        
        tex.LoadRawTextureData(img_bytes);
        tex.Apply();

        isComplete = false;
    }

    /// <summary>
    /// 일반적인 FileStream 을 이용한 byte 넣기 
    /// </summary>
    IEnumerator normal_load()
    {
        string fileNAme = @"test1920_" + workcount % 2 + ".png";
        FileStream fs = new FileStream(fileNAme, FileMode.Open);
        byte[] readed = new byte[fs.Length];
        fs.Read(readed, 0, readed.Length);
        fs.Close();

        tex.LoadImage(readed);
        tex.Apply();
        ++workcount;

        yield return null;
    }

    /// <summary>
    /// 일반적인 WWW 를 이용한 로딩 
    /// </summary>
    IEnumerator www_load()
    {
        WWW www = new WWW("file://test1920_" + workcount % 2 + ".png");

        yield return www;

        Debug.Log(www.isDone);

        tex = www.texture;

        ++workcount;
    }


    /*
     * Background Thread
     * */

    void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        work();
    }

    void work()
    {
        string fileNAme = @"test1920_" + workcount % 2 + ".png";
        Bitmap bitmap = new Bitmap(fileNAme);
        img_size.Width = bitmap.Width;
        img_size.Height = bitmap.Height;

        BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int bufferSize = data.Height * data.Stride;

        img_bytes = new byte[bufferSize];
        

        Marshal.Copy(data.Scan0, img_bytes, 0, img_bytes.Length);
        
        bitmap.UnlockBits(data);
        bitmap.Dispose();

        ++workcount;
    }

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        isComplete = true;
    }



    /*
     * OnRnedring
     * */

    void OnGUI()
    {
        if (GUI.Button(new Rect(100, 0, 200, 30), "BackgroundThread - Marshaling (Raw)"))
        {
            bw.RunWorkerAsync();
            StartCoroutine("cothread");
        }

        if (GUI.Button(new Rect(100, 30, 200, 30), "MainThread - Marshaling(Raw)"))
        {
            work();
            isComplete = true;
            StartCoroutine("cothread");
        }

        if (GUI.Button(new Rect(100, 60, 200, 30), "MainThread - FileStream"))
        {
            StartCoroutine("normal_load");
        }

        if (GUI.Button(new Rect(100, 90, 200, 30), "MainThread - WWW"))
        {
            StartCoroutine("www_load");
        }

        if (tex != null)
        {
            GUI.DrawTexture(new Rect(0, 0, 100, 100), tex);
        }
    }


    // Update is called once per frame
    void Update()
    {
        TestCube.transform.Rotate(16, 1, 1);
    }
}

테스트 를 빌드한 파일은 이것  

b1.zip

이다.


아쉽게도 버튼을 위에서 아래로 테스트 해야 한다. 아마도 LoadRawTextureData 의 로딩 방식이 정말 말처럼 Raw 하여 이미 다른 방식으로 로딩된 텍스쳐를 별다른 처리 없이 Replace 못하나 보다. 


테스트를 한 동영상도 아레에 첨부한다.



결론 : Marshal 를 이용한 방법이 훨씬 빠르다.




Yamecoder 야매코더_
unity3d 2014.04.04 18:21

프로젝트의 파일을 빌드시 Export 하기

유니티를 사용하면서 조금 불편한점 중 하나는 모든 Asset 들을 패키지로 묶어 빌드가 되기 때문에 간단히 이미지파일이나 텍스트파일을 밖으로 빼는것이 불현하다.


그래서 비주얼 스튜디오의 [출력 디렉토리로 복사] 를 흉내 내보기로 했다.


아래의 코드는 Resources 안의 폴더의 특정 위치에 있는 이미지 파일을 읽어 원하는 빌드 폴더에 복사하는 코드 이다.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Assets.Scripts.Snow
{
    public class CopyAssetFiles
    {
        private DirectoryInfo dir;
        public CopyAssetFiles()
        {
            dir = new DirectoryInfo(Environment.CurrentDirectory +"/Snow");
            if(dir.Exists == false)
            {
                dir.Create();
            }

            if(dir.GetFiles().Length < 1 )
            {
                Copy();
            }
        }

        private void Copy()
        {
            Texture2D[] assets = UnityEngine.Resources.LoadAll<Texture2D>("Snow");
            foreach(Texture2D tex in assets)
            {
                byte[] pngfiles = tex.EncodeToPNG();
                Debug.Log(tex.name + " " + pngfiles.Length);
                using(FileStream fs = new FileStream(dir+"/"+tex.name +".png" , FileMode.OpenOrCreate))
                {
                    fs.Write(pngfiles, 0, pngfiles.Length);
                }
            }
        }
    }
}



한가지 문제점은 적어도 한번은 유니티 어플리케이션을 실행하여야 한다는 점이다. 

또 한가지 문제점은 기존의 원본 이미지 와 다른 크기로 복사된다. (순수하게 byte 를 읽어주면 좋으련만...)

Yamecoder 야매코더_
unity3d 2014.03.10 21:03

[Shader] Circle Transition + MousePosition



* 마우스를 클릭한 지점부터 Spread 됨



b2 (2).zip




c#

using UnityEngine;
using System.Collections;

public class Main : MonoBehaviour {

    public Material circle_transition;
    public GameObject car;
    public Texture main_tex;
    public Texture sub_tex;

    private Texture currentTex;

	void Start () {
        circle_transition.SetTexture("_MainTex", main_tex);
        circle_transition.SetTexture("_SubTex", sub_tex);
        QualitySettings.SetQualityLevel(5);
	}

    float Progress = 2.74f;
    float Fuzzy = -1.99f;
    float center_x = 0.5f;
    float center_y = 0.85f;
    void OnGUI()
    {
        RangeComm("Progress", 0, ref Progress, 2.74f, 4f);
        RangeComm("Fuzzy", 30, ref Fuzzy, -1.99f, -1.5f);
        RangeComm("center_x", 60, ref center_x, 0, 1);
        RangeComm("center_y", 90, ref center_y, 0, 1);
    }

    Vector4 position = new Vector4();
    Vector4 ray_position = new Vector4();
	void Update () {

        car.transform.Rotate(0, 0, 0.1f);

        position.x = center_x;
        position.y = center_y;
        circle_transition.SetFloat("_Progress", Progress);
        circle_transition.SetFloat("_FuzzyAmount", Fuzzy);
        //circle_transition.SetVector ("_Center", position);
        circle_transition.SetFloat("_CircleSize", -6.87f);

        if(Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit ;
            if (Physics.Raycast(ray, out hit, Mathf.Infinity))
            {
                ray_position.x = hit.textureCoord.x;
                ray_position.y = hit.textureCoord.y;
                circle_transition.SetVector("_Center", ray_position);

                StartProgressive();
            }
        }

	}

    private iTween prevTween = null;
    private void StartProgressive()
    {
        if (prevTween != null)
            prevTween.dispose();

        prevTween = XTween.To(
            2.74f,
            4.0f,
            3.0f,
            0.0f,
            iTween.EaseType.linear,
            delegate(float v)
            {
                circle_transition.SetFloat("_Progress", v);
            }, 
            delegate
            {
                ChageTextures();
            },
            delegate {
                Debug.Log("dispose!");
                ChageTextures();
            }
            );
    }

    private void ChageTextures()
    {
        Texture2D getMainTex = circle_transition.GetTexture("_MainTex") as Texture2D;
        Texture2D getSubTex = circle_transition.GetTexture("_SubTex") as Texture2D;
        circle_transition.SetTexture("_MainTex", getSubTex);
        circle_transition.SetTexture("_SubTex", getMainTex);
    }

    Texture2D TextureClone(Texture2D org_tex)
    {
        Texture2D cloneTex = new Texture2D(org_tex.width, org_tex.height);
        var color = org_tex.GetPixels(0, 0, org_tex.width, org_tex.height);
        cloneTex.SetPixels(color);
        cloneTex.Apply();
        return cloneTex;
    }

    private float RangeComm(string name , float position_y , ref float v ,float min = 0.0f, float max = 1.0f)
    {
        GUI.Label(new Rect(0, position_y, 40, 20), name);
        v = GUI.HorizontalSlider(new Rect(40, position_y, 160, 20), v, min, max);
        return v;
    }
}







shader 


 

Shader "Custom/Circle_Transition_Position" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_SubTex("sub tex" , 2D) = "white"{}
		_Progress("progress" , float) = 0
		_FuzzyAmount("fuzzy" , float) = 0
		_CircleSize("circle size" , float) = 1
		_Center("center xy" , vector) = (1,1,1,1)
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert

		sampler2D _MainTex;
		sampler2D _SubTex;
		float _Progress;
		float _FuzzyAmount;
		float _CircleSize;
		float4 _Center;
		struct Input {
			float2 uv_MainTex;
			float4 Color;
			float3 worldPos;
		};

		void surf (Input IN, inout SurfaceOutput o) {
					
			float radius = - _FuzzyAmount + _Progress + (_CircleSize + 2.0 + _FuzzyAmount);
			float fromCenter = length(IN.uv_MainTex - _Center);//distance(IN.uv_MainTex , _Center);
			float distFromCircle = fromCenter - radius;
			float4 c1 = tex2D(_MainTex , IN.uv_MainTex);
			float4 c2 = tex2D(_SubTex , IN.uv_MainTex);
			float p = saturate((distFromCircle +  _FuzzyAmount)/(2.0 + _FuzzyAmount));
			
			float4 result = lerp(c2 , c1 , p);
			
			half4 c = result;//(1,1,1,1);//tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}


Yamecoder 야매코더_
unity3d 2014.02.21 18:46

[Shader] Circle Transition

WPF 의 Shazzam shader editor 를 보고 포팅 하였다.



데모 (윈도우):  

build.zip







Surface 쉐이더 이용



 

Shader "Custom/Circle_Transition" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_SubTex("sub tex" , 2D) = "white"{}
		_Progress("progress" , float) = 0
		_FuzzyAmount("fuzzy" , float) = 0
		_CircleSize("circle size" , float) = 1
		_Center("center xy" , vector) = (1,1,1,1)
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma surface surf Lambert

		sampler2D _MainTex;
		sampler2D _SubTex;
		float _Progress;
		float _FuzzyAmount;
		float _CircleSize;
		float4 _Center;
		
		struct Input {
			float2 uv_MainTex;
			float4 Color;
			float3 position;
		};

		void surf (Input IN, inout SurfaceOutput o) {
			float radius = - _FuzzyAmount + _Progress + (_CircleSize + 2.0 + _FuzzyAmount);
			float fromCenter = distance(IN.uv_MainTex , _Center);
			float distFromCircle = fromCenter - radius;
			float4 c1 = tex2D(_MainTex , IN.uv_MainTex);
			float4 c2 = tex2D(_SubTex , IN.uv_MainTex);
			float p = saturate((distFromCircle +  _FuzzyAmount)/(2.0 + _FuzzyAmount));
			
			float4 result = lerp(c2 , c1 , p);
			
			half4 c = result;//(1,1,1,1);//tex2D (_MainTex, IN.uv_MainTex);
			o.Albedo = c.rgb;
			o.Alpha = c.a;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}


Yamecoder 야매코더_
unity3d 2014.02.21 01:42

[Shader]TriBlend + Circle

RGB 채널을 분리하여 3가지 텍스처를 각각 컬러 mask 함.




빌드파일

b2.zip






shader code

Shader "Custom/3Blend_Circle" {
	Properties {
		_Color("main color" , Color) = (1,1,1,1)
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_Tex1 ("color r" , 2D) = "white" {}
		_Tex2 ("color g" , 2D) = "white" {}
		_Tex3 ("color b" , 2D) = "white" {}
		
		_Center("center xy" , vector) = (0,0,0,0)
		_Size("size" , float) = 1
		
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		Pass
		{
			//기존�� ShaderLab 문법을 사용할�� 있다
			//��지만 CGPROGRAM 보다 앞서서 선언이 ��어야 한다
			Blend SrcAlpha OneMinusSrcAlpha
			Lighting On
			Material
			{
				Diffuse[_Color]
				Ambient[_Color]
			}
			
			CGPROGRAM 
// Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs.
#pragma exclude_renderers gles
			#pragma fragment frag 
	
			sampler2D _MainTex;
			sampler2D _Tex1;
			sampler2D _Tex2;
			sampler2D _Tex3;
			float4 _Center;
			float _Size;
			
			struct Input  {
				float2 uv : TEXCOORD0;
				float4 diffuse : COLOR;
			};
			
			half4 frag (Input i):COLOR {
				
				float2 centerNormalized = {_Center.x / 100 , _Center.y/100};
				float2 dir = i.uv - centerNormalized;
				float dist = length(dir) * _Size;
				float angle = atan2(dir.y , dir.x);
				half4 CircleTex = tex2D(_MainTex , centerNormalized - dist);
				
				half4 color1 = tex2D( _Tex1, i.uv.xy );
		        half4 color2 = tex2D( _Tex2, i.uv.xy );
		        half4 color3 = tex2D( _Tex3, i.uv.xy );
				half4 mask = CircleTex;//tex2D( _MainTex, i.uv.xy );
				half4 color = color1 * mask.r + color2 * mask.g + color3 * mask.b;
				
				return color * i.diffuse * 2.0;
			}
			ENDCG // Cg 종료
		}
		
	} 
	FallBack "Diffuse"
}


Yamecoder 야매코더_
unity3d 2014.02.20 16:56

[메모] 유니티 Cg Shader 참고

속성 Syntax들 (더 있을듯 한데,,)

https://docs.unity3d.com/Documentation/Components/SL-Properties.html



Cg 와 연결하는 법

http://docs.unity3d.com/Documentation/Components/SL-PropertiesInPrograms.html



개요

http://chulin28ho.egloos.com/5498022


간단강좌

http://jinhomang.tistory.com/entry/%EC%9C%A0%EB%8B%88%ED%8B%B0-%EC%85%B0%EC%9D%B4%EB%8D%94%EC%9D%98-%EA%B8%B0%EC%B4%88-6


기본 코드 (ShaderLab 부분에서는 코드 끝에 [;] 를 붙이지 않는다)

Shader "Custom/3Blend_Circle" { Properties { _Color("main color" , Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { //기존의 ShaderLab 문법을 사용할 수 있다 //하지만 CGPROGRAM 보다 앞서서 선언이 있어야 한다 Blend SrcAlpha OneMinusSrcAlpha

Lighting On Material { Diffuse[_Color] Ambient[_Color] } CGPROGRAM // Cg 시작 // Upgrade NOTE: excluded shader from OpenGL ES 2.0 because it does not contain a surface program or both vertex and fragment programs. #pragma exclude_renderers gles #pragma fragment frag //Cg 문법을 사용하기 위한 fragment 와 메인함수 frag 를 선언 sampler2D _MainTex; // 상단 Properties 를 연결함 struct Input { //Input float2 uv; }; half4 frag (Input i):COLOR { return tex2D(_MainTex , i.uv); } ENDCG // Cg 종료 } } FallBack "Diffuse" }


Yamecoder 야매코더_
unity3d 2014.02.20 13:40

[메모]Unity3d 다른 GameObject 의 Script Component 접근하기

  var d = GameObject.Find("GuideMovie");   // GuideMovie 라는 GameObject 

        GuideMovie m = d.GetComponent<GuideMovie>(); // GuideMovie.cs 컴포넌트

        Debug.Log(m.TEST);



도  되지만.. 더욱 간단한 방법


var a = GameObject.FindObjectOfType< GuideMovie >();





Yamecoder 야매코더_
unity3d 2013.12.06 14:36

Matrix4x4를 이용한 회전, 스케일 중심점 설정하기


유니티의 Matrix4x4 가 있다.

그리고 GameObject 의 Transform 에 있다.

그런데 , 이것이 읽기 전용이라 당황스러웠다. 이것을 수정하거나 하거나 새롭게 할당 하는것이 불가능하다. 

다른 플랫폼(DirectX , XNA , as3 , WPF 등등.. ) 처럼 행렬 변환을 사용할 수 없다는것이 별도의 프레임워크 제작에 상당히 지장이 되어 해외 커뮤니티를 뒤졌지만 직접적인 접근은 불가 하고 별도의 GameObject 를 이용하여 Pivot 을 설정하라고 한다. 

쉽게말해 CreateEmpty 를 만들어서 상위 컨테이너를 설정하라는거다.


위의 방법은 아무리 생각해도, 불필요한 리소스의 낭비 이다. 단지 Matrix 만 접근하여 Transformation 하면될것을... 이거 오랜만에 발견한 유니티의 단점 되겠다.


그래서 고심끝에 생각한 방법은 바로 그냥 직접 모든 버텍스에 Matrix 연산을 수행하는것이다. 그러면 기존에 알고 있던 지식을 활용할 수 있다.




Matrix4x4 vm = Matrix4x4.identity; void Update () { if (Input.GetKey(KeyCode.A)) { Vector3 pivot = new Vector3( Input.mousePosition.x - Screen.width / 2, Input.mousePosition.y - Screen.height / 2, 0);

//변형된 행렬의 역행렬을 중심점에 적용해줘야 원하는 객체와 같은 위치에서 역할수행이 가능하다 pivot = vm.inverse.MultiplyPoint(pivot); float scale = 1.01f;//Mathf.Sin(c1); print(scale);

//평행이동과 회전, 로테이션을 한후 다시 원래대로 평행이동 하는 일반적인 로직이다 vm *= Matrix4x4.TRS(pivot, Quaternion.EulerAngles(0, 0, 1 * Mathf.PI / 180), new Vector3(scale, scale, 1)); vm *= Matrix4x4.TRS(-pivot, Quaternion.EulerAngles(0, 0, 0 * Mathf.PI / 180), new Vector3(1f, 1f, 1)); sp1.matrixtest(vm); } }


sp1 은 따로 점정4개를 이용하여 만든 Sprite 클래스의 인스턴스 이고 

matrixtext(...) 은 아래와 같다




public void matrixtest(Matrix4x4 m)
        {
            Vector3 p0 = new Vector3(-_width * 0.5f, _height * 0.5f, 0);
            Vector3 p1 = new Vector3(_width * 0.5f, _height * 0.5f, 0);
            Vector3 p2 = new Vector3(-_width * 0.5f, -_height * 0.5f, 0);
            Vector3 p3 = new Vector3(_width * 0.5f, -_height * 0.5f, 0);

            
            verts = new Vector3[4]
            {
                m.MultiplyPoint(p0),
                m.MultiplyPoint(p1),
                m.MultiplyPoint(p2),
                m.MultiplyPoint(p3),
            };
            mesh.vertices = verts;
        }





맥 북 의 화면 녹화기능을 사용하니 프레임이 확 떨어진다... 


Yamecoder 야매코더_
unity3d 2013.10.05 15:14

PixelPerfect 설정하기



3D 공간 상에서 2D 를 표현 하고 그에 적절한 인풋을 받기 위하여 텍스처의 실제 픽셀 사이즈와 투영되는 실제 화면과 같게 만들 필요가 있다.



I would tell you to search your problem as it has been answered hundreds of times, but seeing as I asked the same thing a while ago here's what you need to do:

1. Choose "Point" as the filter mode for your textures.
(This is essential for making your textures sharp)

2. Choose "Advanced" as your "Texture Type" and disable "Generate Mip Maps"
(You don't need them and I think this makes your textures smaller)

3. Choose "RGBA 32 bit" as your "Texture Format"
(You could probably choose a lesser mode and get the same result, I haven't tested)

4. Set your Camera Projection to be "Orthographic
(This makes it 2d)

5. Set your "Orthographic Size" to your screen height / 2
(This makes it so 1 unit in Unity equals 1 pixel on your textures)

6. Either create a quad through script or make a 1x1 unit one in a 3d application
(In 3ds max I set display units and system units to meters and create a 1m^2 plane for this to work)

7. Scale your quad so it's the same size as your texture
(A 64x64 texture needs a 64x64 quad)

I think that's it.

And since you can't set the game screen size in the editor adding a script like this to your camera is perhaps smart as it always ensures your orthographic size is half your height. If there's a screen resize event you can probably put it there, but I haven't checked for one:
Code:  
  1. using UnityEngine;
  2. using System.Collections;
  3.  
  4. public class CameraScale: MonoBehaviour {
  5.  
  6.     void Awake () {
  7.         camera.orthographicSize = Screen.height / 2;
  8.     }
  9. }

 

출처 : http://forum.unity3d.com/threads/82265-Pixel-Perfect-and-Clean-Textures

Yamecoder 야매코더_
unity3d 2013.10.04 10:42

[Unity3D] c# 별도의 클래스를 인스펙터와 연결 [System.Serializable]

출처 : 

http://forum.unity3d.com/threads/183289-C-Not-seeing-class-in-inspector

http://adolys.tistory.com/110





유니티 스크립트의 강점중 하나가 MonoBehaviour를 상속한 클래스를 오브젝트에 컴포넌트로 등록 시키면 , 그 클래스 안의  public Type something; 의 공개 변수가 인스펙터에 실시간으로 반영이 되어 GUI를 통하여 할당이 가능하다는 것이다. 


그런데 c# 으로 할경우 [System.Serializable] 을 해줘야 별도의로 작성된 클래스가 MonoBehaviour를 타고 인스펙터에 표시 된다. 




위의 이미지 처럼 보인다.

Yamecoder 야매코더_
unity3d 2013.08.06 12:27

Unity3d 에 OpenGL 사용하기




유니티에서 2D 가 상당히 아쉬웠다. 정확하게 2D 보다는 코드상으로 그릴수 있는 API 가 없다는 것이 초기 유니티를 접하기전 가장큰 난관이었다.


이런 고충이 있을것이라는것을 이미 알고 있었는지 GL 을 풀어 준것 같다. 물론 실제 Open GL 만큼 모든 API 를 지원하지 않지만 , 간단한 선 , 삼각형 , 사각형쯤은 무리 없는것 같다.


using UnityEngine; using System.Collections; public class Main : MonoBehaviour { // Use this for initialization void Start () { CreateGLMaterial(); } //GL 에 사용할 메터리얼 생성 Material GLMaterial; void CreateGLMaterial() { if( !GLMaterial ) { GLMaterial = new Material( "Shader \"Lines/Colored Blended\" {" + "SubShader { Pass { " + " Blend SrcAlpha OneMinusSrcAlpha " + " ZWrite Off Cull Off Fog { Mode Off } " + " BindChannels {" + " Bind \"vertex\", vertex Bind \"color\", color }" + "} } }" ); GLMaterial.hideFlags = HideFlags.HideAndDontSave; GLMaterial.shader.hideFlags = HideFlags.HideAndDontSave; } } float c = 0f; void OnPostRender() { c += 0.01f; GLMaterial.SetPass(0); GL.PushMatrix(); GL.LoadOrtho(); GL.Begin(GL.TRIANGLES); // 삼각형들 , 3의 배수만큼 버텍스를 찍으면 된다. GL.Color(Color.red); GL.Vertex3(0.50f,0.25f,0); GL.Vertex3(0.25f,0.25f,0); GL.Vertex3(0.375f,0.5f,0); GL.Color(Color.red); GL.Vertex3(0.30f,0.05f,0); GL.Color(Color.yellow); GL.Vertex3(0.05f,0.05f,0); GL.Color(Color.cyan); GL.Vertex3(0.175f,0.1f,0); GL.End(); GL.Begin(GL.QUADS); // 사각형들 , 4의 배수만큼 버텍스를 찍으면 된다. GL.Color(Color.green); GL.Vertex3(0.5f,0.5f,0); GL.Vertex3(0.5f,0.75f,0); GL.Color(Color.red); GL.Vertex3(0.75f,0.75f,0); GL.Vertex3(0.75f,0.5f,0); GL.Color(Color.green); GL.Vertex3(0.4f,0.5f,0); GL.Vertex3(0.4f,0.65f,0); GL.Color(Color.red); GL.Vertex3(0.55f,0.65f,0); GL.Vertex3(0.55f,0.4f,0); GL.End(); GL.Begin(GL.LINES); // 선 , 2의 배수만큼 버택스를 찍으면 된다. GL.Color(Color.blue); GL.Vertex3(Mathf.Sin(c),0,0); GL.Color(Color.red); GL.Vertex3(0.3f,0.75f,0); GL.Vertex3(0.5f,0.2f,0); GL.Color(Color.green); GL.Vertex3(0f,0.2f,0); GL.End(); GL.Begin(GL.TRIANGLE_STRIP); // 삼격형 스트립 , 3개 이상만 찍으면 연결 된다. GL.Color(Color.red); GL.Vertex3(0.50f,0.25f,0); GL.Color(Color.yellow); GL.Vertex3(0.25f,0.25f,0); GL.Color(Color.cyan); GL.Vertex3(0.375f,0.5f,0); GL.Vertex3(0.8f,0.8f,0); GL.Color(Color.red); GL.Vertex3(0.3f,0.7f,0); GL.Color(Color.red); GL.Vertex3(Mathf.Cos(c),0.2f,0); GL.End(); GL.PopMatrix(); } // Update is called once per frame void Update () { } }



아쉬운것은 Line Strip , Triangle fan , 등등등등등 이 없다.

Yamecoder 야매코더_
unity3d 2013.08.01 16:27

유니티의 친절한 Ray cast 사용법





지금까지 내가 경험한 3D 엔진 혹은 3D 를 표방한 엔진 모두 통틀어서 유니티의 Raycast 모델이 가장 간편하고 정확했다.


화면의 [Ray Point] 박스의 좌측 상단이 실제 연산할 2D 포인트 이다.


구현은 아래와 같다.


 

using UnityEngine; using System.Collections; public class Main : MonoBehaviour { GameObject box1; void Start () { //타켓 오브젝트 box1 = GameObject.Find("Cube"); print(box1); } //Raycast 할 Vector3 좌표 Vector3 position = new Vector3(Screen.width/2,Screen.height/2,0); void OnGUI() { // Raycast 의 포인트를 화면에 표시 // 주의점은 GUI 와 좌표계가 다르다. 따라서 Screen.height - position.y 로 처리 한다. GUI.Box( new Rect(position.x , Screen.height - position.y , 50,50) , "Ray\nPoint"); } void Update () { if(Input.GetKeyDown(KeyCode.UpArrow)) { position.y +=10; } else if(Input.GetKeyDown(KeyCode.DownArrow)) { position.y -= 10; } else if(Input.GetKeyDown(KeyCode.LeftArrow)) { position.x -=10; } else if(Input.GetKeyDown(KeyCode.RightArrow)) { position.x += 10; } //실제 Raycast 계산 else if(Input.GetKeyDown(KeyCode.Space)) { print("calculate ray"); Ray ray = Camera.main.ScreenPointToRay(position); RaycastHit hit ; if(Physics.Raycast(ray , out hit)) { if(hit.transform.gameObject == box1) { box1.transform.Rotate(0,10,0); } } } } }



위와 같은 방법으로 간단하게는 Mouse Event 더 나아가서 manipulation도  구현할 수 있다.

Yamecoder 야매코더_
unity3d 2013.08.01 14:34

두개의 카메라를 한 화면에 렌더링




화면처럼 모두 2개의 카메라가 서로 중첩이 되지 않은 2개의 오브젝트를 각각 비추고 있다.

또한 왼쪽 카메라는 스카이박스가 적용되어 있다.


오른쪽 카메라를 왼쪽카메라 위로 올려 보겠다. 즉 , 하나의 화면에 2개의 카메라를 렌더링 하는것이다







왼쪽카메라의 인스펙터 인데 , Clear flags 를 Depth only 로 변경한다. 그리고 실행하게 되면 두개의 카메라가 한 화면에 나온다. 또한 카메라들의 인스펙터안의 Depth 의 순서를 변경하여 렌더링 하는 순서를 제어 할 수 있다. 



이런 식으로 직교투영(2D) 와 원근투영(3D) 를 혼합하여 사용이 가능하여 GUI 와 3D 컨텐츠를 동시에 프로그래밍이 가능하다.



Yamecoder 야매코더_
unity3d 2013.07.31 12:55

웹캠의 활용, 캡처 뜨기




using UnityEngine; using System.Collections; using System.IO; public class WebcamTest : MonoBehaviour { WebCamTexture wtex; Texture2D boxTex; // Use this for initialization void Start () { print("start"); print(this.renderer.material); print(WebCamTexture.devices.Length); //캡처가 저장될 cube Object GameObject box = GameObject.Find("Cube1"); //캡처를 할 실제 텍스쳐 생성과 적용 boxTex = new Texture2D(640,480 , TextureFormat.ARGB32 , false); box.renderer.material.mainTexture = boxTex; if(WebCamTexture.devices.Length < 1) return; wtex = new WebCamTexture(WebCamTexture.devices[0].name , 640 , 480 ,30); this.renderer.material.mainTexture = wtex; wtex.Play(); } void OnGUI() { if(GUI.Button(new Rect(0,0,50,50) , "촬��")) { saveTex(); } } void saveTex() { print(wtex.width + " " + wtex.height + " " + wtex.GetPixels().Length); boxTex.SetPixels(wtex.GetPixels()); //중요 boxTex.Apply(); //이미지의 저장 //byte[] savedata = boxTex.EncodeToPNG(); //FileStream fs = System.IO.File.Create(@"e:\unitytest.png"); //fs.Write(savedata , 0 , savedata.Length); //fs.Close(); } // Update is called once per frame Vector3 rotVec = new Vector3(0,1,0); void Update () { this.transform.Rotate(rotVec); } }



매우 간단하다. 

위의 코드는 상단 이미지의 Sphere 에 붙인것이라 this 가 Sphere 즉 , 윕캠텍스쳐가 붙은 오브젝트 이고 Cube1을 Find 한것인데 , 사실 상당히 안좋은 코드이다. 실전에서는 이렇게 하다간 낭패 본다. 


메인에서 정확하게 딱딱 나눠줘야... 한다.



또한 

boxTex 에서 SetPixel 하고 Apply() 를 해줘야 적용된다. 


'unity3d' 카테고리의 다른 글

유니티의 친절한 Ray cast 사용법  (3) 2013.08.01
두개의 카메라를 한 화면에 렌더링  (3) 2013.07.31
웹캠의 활용, 캡처 뜨기  (0) 2013.07.29
Shader 의 적용부터 프로퍼티 접근까지  (2) 2013.07.29
OGV 변환 하기  (3) 2013.07.17
Custom Plane  (3) 2013.04.21
Yamecoder 야매코더_
unity3d 2013.07.29 18:15

Shader 의 적용부터 프로퍼티 접근까지




쉐이더 작성은 Strumpy 를 이용한다 (이렇게 쉽게 쉐이더를 작성할 수 있다니,, 노벨상 감이다.)



임포트 하고 처음 실행 시키자마자 나오는 기본 예제를 가지고 테스트 해보자. 

이것을 에디터 우측 하단의 File 버튼을 누르면 Export As 가 나온다. 그러면 기본적으로 유니티의 Asset 으로 들어간다. 우선 이것을 basic.shader 라고 저장 하였다.





그리고 Strumpy에서 빠져 나와 유니티의 Asset 에서 Material 을 하나 생성한다.



우선 이름을 basic_M 으로 하겠다.

basic_M 에 basic쉐이더를 드래그엔 드롭하면 작성된 쉐이더의 속성들이 메터리얼에 적용된다.




이 쉐이더에는 5개의 프로퍼티가 있는데 이를 변경하면 실시간으로 비주얼이 바뀜을 확인 할 수 있다.

이제 이 프로퍼티들을 어떻게 프로그래밍을 제어를 하냐 이다. 

유니티 역시 다른 3D 프로그래밍과 별반 차이는 없다. 직접적인 프로퍼티 레퍼런스가 자동 완성 되지는 않고 Get(string) , Set(string) 과 같은 메소드로 파싱을 해서 접근 하는 방식이다.



테스트 코드는 아래와 같다


using UnityEngine; using System.Collections; public class Control : MonoBehaviour { // Use this for initialization void Start () { print("start"); //속성 가져오기 p = this.renderer.material.GetFloat("_RimPower"); } private float p = 0; void OnGUI() { if(GUI.Button(new Rect(0,0,100,100) , "+")) { p += 0.1f; //속성 설정하기 this.renderer.material.SetFloat("_RimPower" , p); } else if(GUI.Button(new Rect(0,100,100,100) , "-")) { p -= 0.1f; this.renderer.material.SetFloat("_RimPower" , p); } GUI.Label(new Rect(100 , 0 , 100,100) , p.ToString()); } // Update is called once per frame void Update () { } }

;






'unity3d' 카테고리의 다른 글

두개의 카메라를 한 화면에 렌더링  (3) 2013.07.31
웹캠의 활용, 캡처 뜨기  (0) 2013.07.29
Shader 의 적용부터 프로퍼티 접근까지  (2) 2013.07.29
OGV 변환 하기  (3) 2013.07.17
Custom Plane  (3) 2013.04.21
unity3d 의 다이나믹 텍스트 렌더링 방법  (1) 2013.04.20
Yamecoder 야매코더_
unity3d 2013.07.29 14:33

OGV 변환 하기

 

유니티에서는 OGV (Theora) 포멧을 사용한다. (공식적으로,, 물론 별도의 동영상 라이브러리를 만들면 다른것도 안될것 없다)

 

그래서 별도의 OGV 변환툴을 사용하여야 한다.

 

http://v2v.cc/~j/ffmpeg2theora/examples.html

 

 

ffmpeg2theora-0.29.exe -> 윈도우

 

ffmpeg2theora-0.29.pkg-> Os x(intel)

 

some examples using ffmpeg2theora:

to convert your DV file, called videoclip.dv to Ogg Theora:

ffmpeg2theora videoclip.dv

this will create an Ogg Theora file called videoclip.dv.ogg.

to encode with another quality, lets say Video Quality 7 and Audio Quality 3:

ffmpeg2theora -v 7 -a 3 videoclip.dv

you can also use presets to encode your video (ffmpeg2theora -p help for more info)

ffmpeg2theora -p preview videoclip.dv

or

ffmpeg2theora -p pro videoclip.dv

on linux you can use ffmpeg2theora to stream to an icecast server:
this needs the latest version of the icecast server and a small tool to send the ogg stream to the icecast server.
a more detailed description can be found in the Streaming HowTo

dvgrab --format raw - | \
	ffmpeg2theora -a 0 -v 5 -f dv -x 320 -y 240 -o /dev/stdout - | \ 
	oggfwd  icecastserver  8000 pwd /theora.ogg

crop the input

ffmpeg2theora --croptop 16 --cropbottom 16 --cropright 32 --cropleft 8 file.avi
Bitcoin: 1NnQ9drLV9sQ5j7utpREWHxTofTXKkH8Wh

 

별도의 옵션 없이 바로 컨버팅 하니 , MP4 의 3배 정도로 용량이 늘어 난다.

Yamecoder 야매코더_
unity3d 2013.07.17 14:27

Custom Plane

기본 Plane 은 세그먼트 조절이 되지 않아 단순 빌보드를 만들고 싶은데 불필요한 부하를 줄수있다.


Assets/Editor 에 아래와 같은 cs 를 작성하고 빌드 하자.

using UnityEngine;
using UnityEditor;
using System.Collections;
 
 
public class CreatePlane : ScriptableWizard
{
 
    public enum Orientation
    {
        Horizontal,
        Vertical
    }
 
    public enum AnchorPoint
    {
        TopLeft,
        TopHalf,
        TopRight,
        RightHalf,
        BottomRight,
        BottomHalf,
        BottomLeft,
        LeftHalf,
        Center
    }
 
    public int widthSegments = 1;
    public int lengthSegments = 1;
    public float width = 1.0f;
    public float length = 1.0f;
    public Orientation orientation = Orientation.Horizontal;
    public AnchorPoint anchor = AnchorPoint.Center;
    public bool addCollider = false;
    public bool createAtOrigin = true;
    public string optionalName;
 
    static Camera cam;
    static Camera lastUsedCam;
 
 
    [MenuItem("GameObject/Create Other/Custom Plane...")]
    static void CreateWizard()
    {
        cam = Camera.current;
        // Hack because camera.current doesn't return editor camera if scene view doesn't have focus
        if (!cam)
            cam = lastUsedCam;
        else
            lastUsedCam = cam;
        ScriptableWizard.DisplayWizard("Create Plane",typeof(CreatePlane));
    }
 
 
    void OnWizardUpdate()
    {
        widthSegments = Mathf.Clamp(widthSegments, 1, 254);
        lengthSegments = Mathf.Clamp(lengthSegments, 1, 254);
    }
 
 
    void OnWizardCreate()
    {
        GameObject plane = new GameObject();
 
        if (!string.IsNullOrEmpty(optionalName))
            plane.name = optionalName;
        else
            plane.name = "Plane";
 
        if (!createAtOrigin && cam)
            plane.transform.position = cam.transform.position + cam.transform.forward*5.0f;
        else
            plane.transform.position = Vector3.zero;
 
		Vector2 anchorOffset;
		string anchorId;
		switch (anchor)
		{
		case AnchorPoint.TopLeft:
			anchorOffset = new Vector2(-width/2.0f,length/2.0f);
			anchorId = "TL";
			break;
		case AnchorPoint.TopHalf:
			anchorOffset = new Vector2(0.0f,length/2.0f);
			anchorId = "TH";
			break;
		case AnchorPoint.TopRight:
			anchorOffset = new Vector2(width/2.0f,length/2.0f);
			anchorId = "TR";
			break;
		case AnchorPoint.RightHalf:
			anchorOffset = new Vector2(width/2.0f,0.0f);
			anchorId = "RH";
			break;
		case AnchorPoint.BottomRight:
			anchorOffset = new Vector2(width/2.0f,-length/2.0f);
			anchorId = "BR";
			break;
		case AnchorPoint.BottomHalf:
			anchorOffset = new Vector2(0.0f,-length/2.0f);
			anchorId = "BH";
			break;
		case AnchorPoint.BottomLeft:
			anchorOffset = new Vector2(-width/2.0f,-length/2.0f);
			anchorId = "BL";
			break;			
		case AnchorPoint.LeftHalf:
			anchorOffset = new Vector2(-width/2.0f,0.0f);
			anchorId = "LH";
			break;			
		case AnchorPoint.Center:
		default:
			anchorOffset = Vector2.zero;
			anchorId = "C";
			break;
		}
 
        MeshFilter meshFilter = (MeshFilter)plane.AddComponent(typeof(MeshFilter));
        plane.AddComponent(typeof(MeshRenderer));
 
        string planeAssetName = plane.name + widthSegments + "x" + lengthSegments + "W" + width + "L" + length + (orientation == Orientation.Horizontal? "H" : "V") + anchorId + ".asset";
        Mesh m = (Mesh)AssetDatabase.LoadAssetAtPath("Assets/Editor/" + planeAssetName,typeof(Mesh));
 
        if (m == null)
        {
            m = new Mesh();
            m.name = plane.name;
 
            int hCount2 = widthSegments+1;
            int vCount2 = lengthSegments+1;
            int numTriangles = widthSegments * lengthSegments * 6;
            int numVertices = hCount2 * vCount2;
 
            Vector3[] vertices = new Vector3[numVertices];
            Vector2[] uvs = new Vector2[numVertices];
            int[] triangles = new int[numTriangles];
 
            int index = 0;
            float uvFactorX = 1.0f/widthSegments;
            float uvFactorY = 1.0f/lengthSegments;
            float scaleX = width/widthSegments;
            float scaleY = length/lengthSegments;
            for (float y = 0.0f; y < vCount2; y++)
            {
                for (float x = 0.0f; x < hCount2; x++)
                {
                    if (orientation == Orientation.Horizontal)
                    {
                        vertices[index] = new Vector3(x*scaleX - width/2f - anchorOffset.x, 0.0f, y*scaleY - length/2f - anchorOffset.y);
                    }
                    else
                    {
                        vertices[index] = new Vector3(x*scaleX - width/2f - anchorOffset.x, y*scaleY - length/2f - anchorOffset.y, 0.0f);
                    }
                    uvs[index++] = new Vector2(x*uvFactorX, y*uvFactorY);
                }
            }
 
            index = 0;
            for (int y = 0; y < lengthSegments; y++)
            {
                for (int x = 0; x < widthSegments; x++)
                {
                    triangles[index]   = (y     * hCount2) + x;
                    triangles[index+1] = ((y+1) * hCount2) + x;
                    triangles[index+2] = (y     * hCount2) + x + 1;
 
                    triangles[index+3] = ((y+1) * hCount2) + x;
                    triangles[index+4] = ((y+1) * hCount2) + x + 1;
                    triangles[index+5] = (y     * hCount2) + x + 1;
                    index += 6;
                }
            }
 
            m.vertices = vertices;
            m.uv = uvs;
            m.triangles = triangles;
            m.RecalculateNormals();
 
            AssetDatabase.CreateAsset(m, "Assets/Editor/" + planeAssetName);
            AssetDatabase.SaveAssets();
        }
 
        meshFilter.sharedMesh = m;
        m.RecalculateBounds();
 
        if (addCollider)
            plane.AddComponent(typeof(BoxCollider));
 
        Selection.activeObject = plane;
    }
}


그리고 빌드를 하면 , 



와 같은 메뉴가 생성 되고 선택을 하면 원하는 옵션들을 볼 수 있다.

Yamecoder 야매코더_
unity3d 2013.04.21 21:58

unity3d 의 다이나믹 텍스트 렌더링 방법

유니티를 해보면서 느낀점이라면 여러가지 이겠지만 , 역시 여타 다른 게임 엔진들과 같은 불편 사항을 가지고 있다.

그것은 바로 텍스트 렌더링 이다. Adobe flash 나 Microsoft WPF , Silverlight 등 과 같은 플랫폼들은 쉽게 텍스트를 렌더링 하고 그것을 변형 과 업데이트가 가능지만 , 유니티를 비롯한 여러 엔진들은 편리한 텍스트 렌더링을 지원하지 않는다. 그래서 대부분 비트맵 폰트의 형태를 만들어서 사용을 하지만 폰트의 크기나 문자수에 따라 부하가 걸리기 마련이다. xna 시절에 WPF 나 윈폼의 텍스트박스 컴포넌트를 비트맵으로 가져와 랜더링 하는 아이디어를 착안하여 유니티의 GUIText 를 통하여 문자열을 실시간으로 불러 와 보겠다.




1) 먼저 GUIText 하나를 만들어 본다.




2) GUIText 가 무사히 나오고 있다.





3) 별도의 텍스쳐를 렌더링할 카메라를 생성한다. 


* 추가로 오디오 리스너를 꺼주어야한다





4) 기존 메인 카메라와 햇갈리지 않도록 이름을 지정 하고 

뎁스를 기존 메인 카메라보다 뒤로 미루어 렌더링우선순위를 뒤로 미룬다.





5) 텍스트가 찍힌 텍스쳐를 입혀볼 큐브를 만든다. 



6) 간단히 빛을 추가하자




7) 다시 GUIText 로 돌아와서 레이어를 이동시키자.


* 추가로 TextLayer 를 만든다.



8) 텍스트를 랜더링할 카메라를 선택하여 컬링마스크의 레이어를 TextLayer 만 선택되도록 한다.




9) 메인 카메라의 컬링 마크스 는 TextLayer 가 랜더링 되지 않도록 한다.



10) GUIText도 텍스트레이어로 이동 시킨다.




11) 그러면 일단 화면에서 텍스트는 사라졌다.



12) 이제 렌더 텍스쳐 하나를 만들자 , 렌더타겟과 비슷한 개념이다. 


* 이름을 간단히 지정했다.



13) 텍스쳐 카메라 의 TextureTarget 을 방금 만든 렌더텍스쳐로 지정한다.




결과 , 큐브에 추가 된다.




14) 이제 배경을 지울 차례이다. 내장 쉐이더의 Bumped Diffuse 를 선택한다.



* 배경이 사라졌다.



좀더 명확히 해보았다.




이제 이 렌더된 텍스트를 실제로 사용하기위해 바이트배열로 저장해보겠다.

여타 다른 엔진들의 렌더타겟 사용법과 비슷하다. 렌더텍스쳐를 액티브 시키고 , 그 영역만큼 텍스쳐로 픽셀을 넣는다.

또한 주의할점은 TextCamera를 업데이트 시키고 그다음 이루어져야 한다. 안그러면 한프레임 전의 화면이 출력된다.


큐브에 사용한 스크립트는 아래와 같다

using UnityEngine;
using System.Collections;
using System.IO;
public class TexControl : MonoBehaviour {

	public GUIText text ;
	public Camera textCamera;
	void Start () {
		
	}
	
	
	int c = 0 ;
	public void capture()
	{
		text.text = (++c) + "STR";
		
		textCamera.Render();
		RenderTexture rt = gameObject.renderer.material.mainTexture as RenderTexture;
		Texture2D capturedText = GetRenderTexturePixels(rt);
		
		byte[]data = capturedText.EncodeToPNG();
		FileStream fs = new FileStream("/Users/superSc1/Desktop/test.png" , FileMode.OpenOrCreate );
		fs.Write(data , 0 , data.Length);
		fs.Close();
		
		Texture2D.DestroyImmediate(capturedText , true); // destory texture2d
		
		
		print("Capture");
	}
	
	
	public Texture2D GetRenderTexturePixels(RenderTexture tex)
	{
		RenderTexture.active = tex;
		
		Texture2D tempTex = new Texture2D(tex.width , tex.height );
		tempTex.ReadPixels( new Rect(0,0,tex.width , tex.height) , 0 , 0);
		tempTex.Apply();
		
		return tempTex;
	}
	
	void Update () {
		if(Input.GetKeyDown(KeyCode.C))
		{
			capture();
		}
	}
}



Yamecoder 야매코더_
unity3d 2013.04.20 23:55

두개의 텍스쳐를 자연그럽게 교체하기



TextureChange.shader

Shader "TextureChange" {
Properties {
	_Blend ("Blend", Range (0, 1) ) = 0.5 
	_Color ("Main Color", Color) = (1,1,1,1)
	_MainTex ("Texture 1", 2D) = "white" {}
	_Texture2 ("Texture 2", 2D) = ""
	_BumpMap ("Normalmap", 2D) = "bump" {}
}

SubShader {
	Tags { "RenderType"="Opaque" }
	LOD 300
	Pass {
		SetTexture[_MainTex]
		SetTexture[_Texture2] { 
			ConstantColor (0,0,0, [_Blend]) 
			Combine texture Lerp(constant) previous
		}		
	}

	CGPROGRAM
	#pragma surface surf Lambert
	
	sampler2D _MainTex;
	sampler2D _BumpMap;
	fixed4 _Color;
	sampler2D _Texture2;
	float _Blend;
	
	struct Input {
		float2 uv_MainTex;
		float2 uv_BumpMap;
		float2 uv_Texture2;
		
	};
	
	void surf (Input IN, inout SurfaceOutput o) {
		fixed4 t1 = tex2D(_MainTex, IN.uv_MainTex) * _Color;
		fixed4 t2 = tex2D (_Texture2, IN.uv_MainTex) * _Color;
		
		o.Albedo = lerp(t1, t2, _Blend);
		o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
	}
	ENDCG  
	}
	
	FallBack "Diffuse"
}



object c# script

using UnityEngine;
using System.Collections;

public class Sh1 : MonoBehaviour {

	Texture tx1;
	Texture tx2;
	Texture newTx;
	bool triggerChange = false;
	float blendRange = 1.0f;
	
	void Start () {
		
		
		//texture load
		tx1 = Resources.Load("img") as Texture;
		print(tx1);
		
		tx2 = Resources.Load("backHD") as Texture;
		print(tx2);
		
		
		gameObject.renderer.material.SetFloat("_Blend" , blendRange);//initialize shader
	}
	
	public void changeTexture(int arg)
	{
		
		if(arg == 0)
			newTx = tx1;
		else if(arg ==1)
			newTx = tx2;
		
		gameObject.renderer.material.mainTexture = newTx;
		triggerChange = true;
	}

	
	private int testC = 0;
	void Update () {
		
		
		if(triggerChange == true)
		{
			blendRange = blendRange - 0.01f; //change speed
			
			gameObject.renderer.material.SetFloat("_Blend" , blendRange);
			
			if(blendRange <= 0)
			{
				triggerChange = false;
				blendRange = 1.0f;
				gameObject.renderer.material.SetTexture("_Texture2" , newTx);
				gameObject.renderer.material.SetFloat("_Blend" , 1);
				
				print("changeComplete");
			}
		}
		
		
		if(Input.GetKeyDown(KeyCode.A))
		{
			print("change!");
			changeTexture(testC % 2);
			
			++testC;
		}
		gameObject.transform.Rotate(Vector3.up , 1f);
	}
}



유니티에서 새로운 쉐이더를 에셋에 추가 하고 위의 쉐이더를 작성한 다음 해당 오브젝트로 드래그 한다.

그럼 적용 된다. 그다음 c# 본문 에서처럼 프로퍼티에 접근이 가능하다.


A  키를 누르면 번갈아서 텍스쳐가 교체된다.


출처 : http://www.sundh.com


Yamecoder 야매코더_
unity3d 2013.04.20 11:05
Powerd by Tistory, designed by criuce
rss