Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 6개 있습니다.)
Graphics: 15. Unity - shader의 World matrix(unity_ObjectToWorld)를 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11633

Graphics: 17. Unity - World matrix(unity_ObjectToWorld)로부터 TRS(이동/회전/크기) 행렬로 복원하는 방법
; https://www.sysnet.pe.kr/2/0/11637

Graphics: 18. Unity - World matrix(unity_ObjectToWorld)로부터 Position, Rotation, Scale 값을 복원하는 방법
; https://www.sysnet.pe.kr/2/0/11640

Graphics: 22. Unity - shader의 Camera matrix(UNITY_MATRIX_V)를 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11692

Graphics: 23. Unity - shader의 원근 투영(Perspective projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11695

Graphics: 25. Unity - shader의 직교 투영(Orthographic projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11700




Unity - shader의 원근 투영(Perspective projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성

지난 글에서 월드 행렬과 카메라 행렬을 수작업으로 구성해 봤으니,

Unity - shader의 World matrix(unity_ObjectToWorld)를 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11633

Unity - shader의 Camera matrix(UNITY_MATRIX_V)를 수작업으로 구성
; https://www.sysnet.pe.kr/2/0/11692

이제 마지막으로 투영 행렬을 구성해 볼 차례입니다. ^^




투영 행렬을 구성하는 요소는 Unity Inspector 창에 보이는 "Field of View", "Clipping Planes"와 게임 화면이 실행되는 윈도우의 "가로/세로" 비율이 됩니다. C# 스크립트에서 이에 대한 값은 각각 다음과 같이 구할 수 있습니다.

Camera camera = Camera.main;

float aspect = camera.aspect; // 또는 (float)camera.pixelWidth / camera.pixelHeight;

float fov = camera.fieldOfView;
float near = camera.nearClipPlane;
float far = camera.farClipPlane;

여기서 Field of View는 다음과 같이 Unity에서 카메라로부터 상하로 퍼져 나가는 시야각을 의미하며 화각 또는 FOV라고 줄여서 말하기도 합니다. (Unity 에디터의 기본값은 60도)

projection_matrix_1.png

near는 기본 값이 0.3인데 월드 좌표계 기준으로 카메라로부터 0.3 만큼 떨어진 "근-평면"의 위치를 의미합니다. Scene을 새로 생성하면 카메라의 기본 위치값이 (0, 1, -10) 좌표이므로 근-평면은 (0, 1, -9.7) 좌표에 위치하게 됩니다.

far는 기본 값이 1000이고 월드 좌표계 기준으로 카메라로부터 1000 만큼 떨어진 "원-평면"의 위치를 의미합니다. 마찬가지로 Scene의 기본 값 상태일 때 원-평면은 (0, 1, 990) 좌표에 위치하게 됩니다. 아래는 유니티 편집 화면에서의 근-평면과 원-평면의 모습을 보여줍니다.

projection_matrix_2.png

FOV와 Near, Far 값은 개발자가 제어할 수 있지만 화면 비율(aspect 값)은 딱히 조정할 수 없습니다. 이건 사용자가 게임을 실행할 때 보통 전체 화면으로 실행하기 때문에 윈도우의 width, height가 고정되거나 "창 모드"로 게임을 실행했을지라도 사용자가 임의로 변경하는 것이기 때문에 개발자 입장에서는 그때마다 aspect 비율을 투영 행렬에 잘 반영하면 됩니다.

이 값들로부터 투영 행렬을 만드는 것은 다음의 책을 보면 잘 나옵니다.

유니티로 배우는 게임 수학  기초 개념부터 모바일까지, 게임 개발에 필요한 수학 원리 설명서 
; http://www.yes24.com/24/goods/30119802

자세하게 수학적인 설명과 곁들여 설명하고 있으므로 그 부분에 대해서는 책을 참고하시고, 이 글에서는 해당 투영 행렬을 Unity shader에서 어떻게 구성할 수 있는지에 대한 내용만 알아보겠습니다.

우선, 책에 있는 프로젝션 변환 행렬 P는 다음과 같이 이뤄진다고 소개하고 있습니다.

${
P = \begin{bmatrix} \frac {2n} {r - l} & 0 & \frac {r + l} {r - l} & 0 \\ 0 & \frac {2n} {t - b} & \frac {t + b} {t - b} & 0 \\ 0 & 0 & \frac {-(f + n)} {f - n} & \frac {-2fn} {f - n} \\ 0 & 0 & -1 & 0 \end{bmatrix}
}$


하지만, 저 투영 행렬은 OpenGL의 관례를 따라 나타낸 것이고 각각의 플랫폼에 따른 shader 상의 투영 행렬은 다르다고 합니다. 일례로 (DirectX를 사용하는) 윈도우 데스크톱 환경의 경우에는 다음과 같은 투영 행렬이 사용된다고 설명합니다.

${
P = \begin{bmatrix} \frac {2n} {r - l} & 0 & \frac {r + l} {r - l} & 0 \\ 0 & \frac {-2n} {t - b} & -\frac {t + b} {t - b} & 0 \\ 0 & 0 & \frac {-f} {f - n} & \frac {-fn} {f - n} \\ 0 & 0 & -1 & 0 \end{bmatrix}
}$


수식에 사용된 변수의 의미는 다음과 같습니다.

l == 근-평면의 좌측 끝의 x 좌표
r == 근-평면의 우측 끝의 x 좌표
b == 근-평면의 하단 끝의 y 좌표
t == 근-평면의 상단 끝의 y 좌표
n == 원점으로부터 근-평면까지의 거리
f == 원점으로부터 원-평면까지의 거리

여기서 n과 f는 이미 Unity Inspector 창에서 Camera 객체의 값으로부터 설정된 바로 그 값입니다. 그리고 나머지 left, right, top, bottom의 값은 FOV에 지정된 각을 이용해 탄젠트 삼각함수로 구할 수 있습니다. 가령 top 값은 근-평면 하단 끝의 y 좌표이므로 x축 기준으로 y-z 평면으로 봤을 때,

projection_matrix_3.png

직각 삼각형의 빗변과 밑변의 각도는 Field of View 60도에서 절반인 30도이고 밑변의 길이가 0.3임을 알고 있으므로 top과 bottom은 다음의 공식으로 알아낼 수 있습니다.

top = tan(30°) * near
    = tan(DegreeToRadian(30°)) * near
bottom = -top;

top과 bottom은 쌍을 이루니 당연히 bottom은 -top이 됩니다. 반면 left와 right의 경우에는 FOV를 이용할 수 없습니다. FOV는 시야의 상하각이기 때문인데, 대신 aspect 값이 있으므로 이를 이용해 top을 aspect와 곱해 구할 수 있습니다.

float right = (top * aspect);
float left = -right;

실제로 Unity C# 스크립트로부터 camera.projectionMatrix와 GL.GetGPUProjectionMatrix(camera.projectionMatrix, true)로 구한 행렬의 값은 다음과 같은 식으로 나옵니다. (Unity 기본 Scene 상태를 가정합니다.)

aspect  1.323475        // 사용자의 환경에 따라 변경
tangentFov 0.5773503    // 기본 Scene 상태인 경우 
near 0.3                // 기본 Scene 상태인 경우 
far 1000                // 기본 Scene 상태인 경우 

top 0.1732051
bottom -0.1732051
right   0.2292326
left    -0.2292326

이렇게 해서 소스가 준비되었군요. ^^




이제 Unity C# 스크립트에서 실제 사용하고 있는 행렬을 보겠습니다.

camera.projectionMatrix
1.30871 0.00000  0.00000  0.00000
0.00000 1.73205  0.00000  0.00000
0.00000 0.00000 -1.00060 -0.60018
0.00000 0.00000 -1.00000  0.00000

GL.GetGPUProjectionMatrix(camera.projectionMatrix, true)
1.30871 0.00000 0.00000 0.00000
0.00000 -1.73205 0.00000 0.00000
0.00000 0.00000 0.00030 0.30009
0.00000 0.00000 -1.00000 0.00000

그런데 대충 봐도, (r+l) / (r-l)을 나타내는 projectionMatrix[0,2]의 값이 0인 것을 보면 완전히 똑같은 것은 아닌 것 같습니다. shader에서도 이렇게 쓰고 있는지 다음과 같은 코드로 테스트할 수 있습니다.

v2f vert(appdata v)
{
    float4 pos;

    v2f o;

    float4x4 m = UNITY_MATRIX_P;
    float4x4 projectionMatrix;

    projectionMatrix[0] = float4(m[0].x, 0,      0,      0);
    projectionMatrix[1] = float4(0,      m[1].y, 0,      0);
    projectionMatrix[2] = float4(0,      0,      m[2].z, m[2].w);
    projectionMatrix[3] = float4(0,      0,      -1,     0);

    pos = mul(unity_ObjectToWorld, v.vertex);
    pos = mul(UNITY_MATRIX_V, pos);
    pos = mul(projectionMatrix, pos);

    o.vertex = pos;

    return o;
}

camera.projectionMatrix 및 GL.GetGPUProjectionMatrix의 구조와 동일한 위치의 값만 설정했는데 정상적으로 투영 행렬로 동작하는 것을 확인할 수 있으며 즉, Unity shader에서 사용하는 투영 행렬은 다음의 2개로 정해집니다.

[OpenGL을 따르는 투영 행렬]

${
P = \begin{bmatrix} \frac {2n} {r - l} & 0 & 0 & 0 \\ 0 & \frac {2n} {t - b} & 0 & 0 \\ 0 & 0 & \frac {-(f + n)} {f - n} & \frac {-2fn} {f - n} \\ 0 & 0 & -1 & 0 \end{bmatrix}
}$

[실행 환경에 맞는 투영 행렬 - 아래는 DirectX를 사용하는 윈도우 환경에서의 투영 행렬]

${
P = \begin{bmatrix} \frac {2n} {r - l} & 0 & 0 & 0 \\ 0 & \frac {-2n} {t - b} & 0 & 0 \\ 0 & 0 & \frac {-f} {f - n} & \frac {-fn} {f - n} \\ 0 & 0 & -1 & 0 \end{bmatrix}
}$


그럼 끝났군요. ^^ 재료를 이용해 다음과 같이 투영 행렬을 구할 수 있습니다.

Matrix4x4 myProjection1 = new Matrix4x4();
myProjection1[0, 0] = (2 * near) / (right - left);
myProjection1[1, 1] = (2 * near) / (top - bottom);
myProjection1[2, 2] = -(far + near) / (far - near);
myProjection1[2, 3] = -(2 * far * near) / (far - near);
myProjection1[3, 2] = -1;

Matrix4x4 myProjection2 = new Matrix4x4();
myProjection2[0, 0] = (2 * near) / (right - left);
myProjection2[1, 1] = -(2 * near) / (top - bottom);
myProjection2[2, 2] = -far / (far - near);
myProjection2[2, 3] = -(far * near) / (far - near);
myProjection2[3, 2] = -1;

바로 저 2개의 행렬들은 Unity C# 스크립트에서 다음의 코드로 대응합니다.

myProjection1 == camera.worldToCameraMatrix
myProjection2 == GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);

또한 우리가 구한 l, r, b, t, n, f의 값들이 실제로 camera.worldToCameraMatrix.decomposeProjection 값들과 동일합니다.

camera.projectionMatrix.decomposeProjection
		left	-0.2292326
		right	0.2292326
		bottom	-0.1732051
		top	    0.1732051	
		zNear	0.3	
		zFar	1000.134	

GL.GetGPUProjectionMatrix(camera.projectionMatrix, true).decomposeProjection
		left	0.2293702	
		right	-0.2293702	
		bottom	-0.1733091	
		top	    0.1733091	
		zNear	-0.3001801	
		zFar	0.3




다음 단계로 Unity shader에서 사용하는 투영 행렬이 camera.projectionMatrix 인지, GL.GetGPUProjectionMatrix 반환 값인지는 다음과 같은 코드로 쉽게 확인할 수 있습니다.

using UnityEngine;

[ExecuteInEditMode]
public class SetMatrix : MonoBehaviour {

    void Start () {
    }

    void Update () {
        Camera camera = Camera.main;

        Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(camera.projectionMatrix, true);
        Shader.SetGlobalMatrix("_projectionMatrix1", projectionMatrix);

        Shader.SetGlobalMatrix("_projectionMatrix2", camera.projectionMatrix);
    }
}

float4x4 _projectionMatrix;
float4x4 _projectionMatrix2;

v2f vert(appdata v)
{
    float4 pos;

    v2f o;

    pos = mul(unity_ObjectToWorld, v.vertex);
    pos = mul(UNITY_MATRIX_V, pos);

    pos = mul(_projectionMatrix1, pos);
    // pos = mul(_projectionMatrix2, pos);

    o.vertex = pos;

    return o;
}

실제로 실행해 보면 GL.GetGPUProjectionMatrix가 반환한 투영 행렬이 정상적으로 동작하는 것을 볼 수 있습니다. 약간 혼란스러운 점이 있다면, C# 스크립트에서 넘겨줄 때의 행렬 값이 다음과 같은 반면,

GL.GetGPUProjectionMatrix

0.97183  0.00000  0.00000 0.00000
0.00000 -1.73205  0.00000 0.00000
0.00000  0.00000  0.00030 0.30009
0.00000  0.00000 -1.00000 0.00000

"Visual Studio Graphics Analyzer"로 디버깅 환경의 Watch 창에서 GL.GetGPUProjectionMatrix의 값을 보면 다음과 같다는 것입니다.

_projectionMatrix1      float4x4
    _projectionMatrix1[0]   x = 0.971829400, y = 0.000000000, z = 0.000000000, w = 0.000000000
    _projectionMatrix1[1]   x = 0.000000000, y = -1.732051000, z = 0.000000000, w = 0.000000000
    _projectionMatrix1[2]   x = 0.000000000, y = 0.000000000, z = 0.000300050, w = -1.000000000
    _projectionMatrix1[3]   x = 0.000000000, y = 0.000000000, z = 0.300090000, w = 0.000000000

즉, 전치가 되어 있는데 이것은 아마도 Visual Studio의 디버거 창이 자동으로 전치를 해주는 것인지? 아니면 메모리 상의 값을 읽을 때 열/행우선을 잘못 판단한 것인지는 알 수 없으나 C# 스크립트 상에서의 디버거 값이 올바른 형식입니다.

정리해 보면, Unity에서 제공하는 투영 행렬은 2가지가 있습니다.

  • camera.projectionMatrix == OpenGL을 따르는 형식
  • GL.GetGPUProjectionMatrix == Unity 프로그램이 실행되는 환경에 부합하는 투영 행렬(일례로 위에서의 _projectionMatrix1 값은 DirectX 11을 사용하는 윈도우 환경에서 유효한 투영 행렬)

휴~~~ 이것으로 대충 정리가 되었군요. ^^ 행렬의 생성 규칙과 그 값의 decomposeProjection을 알게 되었으니 이제 나머지 단계는 unity shader에서 decomposeProjection에 해당하는 값을 어떻게 구하느냐에 달려 있습니다.




unity shader에서 근-평면/원-평면은 내장 변수를 통해 값을 구할 수 있습니다.

_ProjectionParams float4

x is 1.0 (or ?1.0 if currently rendering with a flipped projection matrix)
y is the camera’s near plane
z is the camera’s far plane
w is 1/FarPlane

float nearPlane = _ProjectionParams.y;
float farPlane = _ProjectionParams.z;

// 기본값인 경우 nearPlane == 0.3, farPlane == 1000

또한 aspect 비율도 _ScreenParams 내장 변수를 통해 구할 수 있습니다.

float width = _ScreenParams.x;
float height = _ScreenParams.y;

float aspect = width / height;

문제는, FOV 값인데 이것은 unity가 내장 변수로 제공하지 않습니다. 대신 기존 UNITY_MATRIX_P로부터 구해올 수는 있습니다. 이에 대한 계산이 재미있는데요. ^^ 이전에 행렬을 구하는 것에서 (0,0)의 값은 다음과 같이 이뤄집니다.

[0, 0] = (2 * near) / (right - left);

그런데 검색해 보면 다음의 소스 코드를 통해,

https://github.com/g-truc/glm/blob/master/glm/ext/matrix_clip_space.inl

template<typename T>
GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar)
{
    assert(abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));

    T const tanHalfFovy = tan(fovy / static_cast<T>(2));

    mat<4, 4, T, defaultp> Result(static_cast<T>(0));
    Result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
    Result[1][1] = static_cast<T>(1) / (tanHalfFovy);
    Result[2][2] = (zFar + zNear) / (zFar - zNear);
    Result[2][3] = static_cast<T>(1);
    Result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
    return Result;
}

"1 / (aspect * tanHalfFovy) = UNITY_MATRIX_P[0].x"와 같다는 것을 알 수 있습니다. 따라서 각도는 다음과 같이 구할 수 있습니다.

aspect * tanHalfFovy * UNITY_MATRIX_P[0].x = 1
tanHalfFovy = 1 / (aspect * UNITY_MATRIX_P[0].x)

radian_FOV = arctan(1 / (aspect * UNITY_MATRIX_P[0].x))

자, 그럼 다 끝났군요. ^^ 이제 다음과 같이 UNITY_MATRIX_P를 재조립해 적용할 수 있습니다.

Shader "Unlit/NewUnlitShader"
{
    Properties
    {
    }
    SubShader
    {
        Tags{ "LightMode" = "ForwardBase" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma enable_d3d11_debug_symbols

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            float4x4 _projectionMatrix;

            v2f vert(appdata v)
            {
                float4 pos;

                v2f o;

                float nearPlane = _ProjectionParams.y;
                float farPlane = _ProjectionParams.z;
                
                float width = _ScreenParams.x;
                float height = _ScreenParams.y;
                float aspect = width / height;

                float4x4 m = UNITY_MATRIX_P;

                float halfFov = atan2(1, aspect * m[0].x);

                float top = tan(halfFov) * nearPlane;
                float bottom = -top;

                float right = (top * aspect);
                float left = -right;

                // DirectX를 사용하는 윈도우 환경에서의 투영 행렬
                float p00 = (2 * nearPlane) / (right - left);
                float p11 = -(2 * nearPlane) / (top - bottom);
                float p22 = -farPlane / (farPlane - nearPlane);
                float p23 = -(farPlane * nearPlane) / (farPlane - nearPlane);

                float4x4 projectionMatrix;
                projectionMatrix[0] = float4(p00, 0,   0,   0);
                projectionMatrix[1] = float4(0,   p11, 0,   0);
                projectionMatrix[2] = float4(0,   0,   p22, p23);
                projectionMatrix[3] = float4(0,   0,   -1,  0);

                pos = mul(unity_ObjectToWorld, v.vertex);
                pos = mul(UNITY_MATRIX_V, pos);
                pos = mul(projectionMatrix, pos);
                // pos = mul(UNITY_MATRIX_P, pos);

                o.vertex = pos;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return fixed4(1, 0, 0, 0);
            }

            ENDCG
        }
    }
}




참고로 https://github.com/g-truc/glm/blob/master/glm/ext/matrix_clip_space.inl 소스 코드를 보면 알 수 있지만 동일한 값을 다음과 같이 다르게 구하고 있습니다.

projectionMatrix[0, 0] = (2 * near) / (right - left);
                       = 1.0 / (aspect * halfFov)

projectionMatrix[1, 1] = -(2 * near) / (top - bottom);
                       = -1.0 / halfFov;

projectionMatrix[2, 2] = -far / (far - near);
projectionMatrix[2, 3] = -(far * near) / (far - near);
projectionMatrix[3, 2] = -1;

2개의 공식이 왜 같은지는 다음과 같이 풀어 보면 쉽게 이해할 수 있습니다.

(2 * near) / (right - left)
 = (2 * near) / (right * 2) // 어차피 left == -right이므로.
 = near / right             // 공통 인수 2 약분
 = near / (top * aspect)    // right = top * aspect이므로.
 = near / (halfFov * near * aspect) // top = halfFov * near
 = 1 / (halfFov * aspect)   // 공통 인수 near 약분

-(2 * near) / (top - bottom)
 = -(2 * near) / (2 * top)  // 어차피 bottom == -top이므로.
 = -near / top              // 공통 인수 2 약분
 = -near / (halfFov * near) // top = halfFov * near
 = -1 / halfFov             // 공통 인수 near 약분

그러니까 결국, left, right, bottom, top을 구할 필요가 없었던 것입니다. 따라서 shader에서의 소스 코드는 다음과 같이 더 간단해집니다.

v2f vert(appdata v)
{
    float4 pos;

    v2f o;

    float nearPlane = _ProjectionParams.y;
    float farPlane = _ProjectionParams.z;

    float width = _ScreenParams.x;
    float height = _ScreenParams.y;
    float aspect = width / height;

    float4x4 m = UNITY_MATRIX_P;

    float halfFov = atan2(1, aspect * m[0].x);

    // DirectX를 사용하는 윈도우 환경에서의 투영 행렬
    float p00 = 1 / (tan(halfFov) * aspect);
    float p11 = -1 / tan(halfFov);
    float p22 = -farPlane / (farPlane - nearPlane);
    float p23 = -(farPlane * nearPlane) / (farPlane - nearPlane);

    float4x4 projectionMatrix;
    projectionMatrix[0] = float4(p00, 0,   0,   0);
    projectionMatrix[1] = float4(0,   p11, 0,   0);
    projectionMatrix[2] = float4(0,   0,   p22, p23);
    projectionMatrix[3] = float4(0,   0,   -1,  0);

    pos = mul(unity_ObjectToWorld, v.vertex);
    pos = mul(UNITY_MATRIX_V, pos);
    pos = mul(projectionMatrix, pos);
    // pos = mul(UNITY_MATRIX_P, pos);

    o.vertex = pos;

    return o;
}

그 외에 여유가 되시면 Unity가 아닌 실제 카메라 영상을 기준으로 한 다음의 설명도 읽어보시고. ^^

카메라 캘리브레이션 (Camera Calibration)
; http://darkpgmr.tistory.com/32?category=460965




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 9/22/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1756정성태9/23/201427493기타: 48. NVidia 제품의 과다한 디스크 사용 [2]
1755정성태9/22/201434281오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424683VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420621오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441072Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438962.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423837.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423736.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425390개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428515오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426108.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423044개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431070.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420985오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426983개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421338.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432526.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426576.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422067.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419772VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425591VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418265.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419932오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426367.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434486Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427089개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...