[DirectX11] 075~77 Lighting, HLSL
DirectX 11에서 Lighting을 구현하려면, HLSL(고수준 셰이더 언어)을 사용하여 셰이더 코드를 작성해야 한다. HLSL은 Direct3D에서 셰이더 프로그래밍을 위해 사용되는 언어로, 셰이더 코드를 작성하고 컴파일하여 GPU에서 실행할 수 있는 바이너리 코드로 변환한다.
목차
Lighting
Lighting을 구현하는 방법은 다양하지만, 일반적으로는 Phong 또는 Blinn-Phong Lighting 모델을 사용한다. 이 모델은 ambient, diffuse, specular lighting을 사용하여 표면의 색상을 계산한다. 각각의 조명 구성 요소는 다음과 같이 계산된다.
Ambient lighting
float4 ambientColor = float4(0.2, 0.2, 0.2, 1.0); // ambient lighting의 색상값
float4 ambientLight = ambientColor * materialColor; //ambient lighting과 material color를 곱해서 계산
Diffuse lighting
float3 lightDirection = normalize(float3(0.0, 1.0, 0.0)); // 조명의 방향 벡터
float4 diffuseColor = float4(1.0, 1.0, 1.0, 1.0); // diffuse lighting의 색상값
float4 diffuseLight = saturate(dot(surfaceNormal, lightDirection)) * diffuseColor * materialColor; //surface normal(표면 법선 벡터)와 조명의 방향 벡터의 내적, diffuse lighting과 material color를 곱해서 계산
Specular lighting
float3 eyeDirection = normalize(eyePosition - surfacePosition); // 눈의 방향 벡터
float3 reflectDirection = reflect(-lightDirection, surfaceNormal); // 조명의 반사 방향 벡터
float4 specularColor = float4(1.0, 1.0, 1.0, 1.0); // specular lighting의 색상값
float4 specularLight = pow(saturate(dot(reflectDirection, eyeDirection)), materialShininess) * specularColor * materialColor; // 반사 방향 벡터와 눈의 방향 벡터의 내적, shininess(광택 정도)의 제곱, specular lighting과 material color를 곱해서 계산
이러한 Lighting 요소들은 조명과 재질(물체의 속성)에 따라 다양하게 계산될 수 있다. 이러한 HLSL 코드는 셰이더에서 조명과 재질을 계산하는 데 사용된다.
Shader | |
Light.fx Lighting.fx |
|
UnitTest | |
Lighting | |
LightingDemo.h .cpp 생성 |
빛 연산
Diffuse Color x Texture x NdotL(Lambert)
Specular Color x Texture x (Phong)
Light(광원에서 쏘아진 빛), Reflection(반사), Eye(시야로 가는선) 이 있을때
- 내적 dot(Reflection, Eye)를 구한 뒤
- power(R dot E, 상수값) 를 구한다.
- pow(0.5, 10)이라면 0.5^10
- 상수값이 커질수록 원(=범위)은 커지지만 값을 흐려진다.
- 상수값이 작을수록 원(=범위)은 작아지지만 빛이 강해진다.
윤곽선 구하기
윤곽선(=외각선)을 구하는 방법
- 눈(Eye)의 방향
- Normal의 방향
- 두 개를 사용하여 구할 수 있다.
바라보는 시점의 외각 = Normal 벡터와의 내적
Global.fx
Global.fx
float3 ViewPosition()
{
return ViewInverse._41_42_43; //카메라 변환되었던 값을 역행렬로 카메라 원래 위치를 구함
}
카메라의 원본 위치를 구한다.
Light.fx
Light.fx
struct LightDesc
{
float4 Ambient;
float4 Specular;
float3 Direction;
float Padding;
float3 Position;
};
cbuffer CB_Light
{
LightDesc GlobalLight;
};
Texture2D DiffuseMap;
Texture2D SpecularMap;
Texture2D NormalMap;
TextureCube SkyCubeMap;
Texture2D ShadowMap;
SamplerComparisonState ShadowSampler;
struct MaterialDesc
{
float4 Ambient;
float4 Diffuse;
float4 Specular;
float4 Emissive;
};
cbuffer CB_Material
{
MaterialDesc Material;
};
MaterialDesc MakeMaterial()
{
MaterialDesc output;
output.Ambient = float4(0, 0, 0, 0);
output.Diffuse = float4(0, 0, 0, 0);
output.Specular = float4(0, 0, 0, 0);
output.Emissive = float4(0, 0, 0, 0);
return output;
}
float3 MaterialToColor(MaterialDesc result)
{
return (result.Ambient + result.Diffuse + result.Specular + result.Emissive).rgb;
}
void AddMaterial(inout MaterialDesc result, MaterialDesc val)
{
result.Ambient += val.Ambient;
result.Diffuse += val.Diffuse;
result.Specular += val.Specular;
result.Emissive += val.Emissive;
}
void Texture(inout float4 color, Texture2D t, float2 uv, SamplerState samp)
{
float4 sampling = t.Sample(samp, uv);
color = color * sampling;
}
void Texture(inout float4 color, Texture2D t, float2 uv)
{
Texture(color, t, uv, LinearSampler);
}
void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition)
{
output = MakeMaterial();
float3 direction = -GlobalLight.Direction;
float NdotL = dot(direction, normalize(normal));
output.Ambient = GlobalLight.Ambient * Material.Ambient;
float3 E = normalize(ViewPosition() - wPosition);
[flatten]
if(NdotL > 0.0f)
{
output.Diffuse = Material.Diffuse * NdotL;
[flatten]
if(Material.Specular.a > 0.0f)
{
float3 R = normalize(reflect(-direction, normal));
float RdotE = saturate(dot(R, E));
float specular = pow(RdotE, Material.Specular.a);
output.Specular = Material.Specular * specular * GlobalLight.Specular;
}
}
[flatten]
if(Material.Emissive.a > 0.0f)
{
float NdotE = dot(E, normalize(normal));
float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
output.Emissive = Material.Emissive * emissive;
}
}
#define MAX_POINT_LIGHTS 256
struct PointLight
{
float4 Ambient;
float4 Diffuse;
float4 Specular;
float4 Emissive;
float3 Position;
float Range;
float Intensity;
float3 Padding;
};
cbuffer CB_PointLights
{
uint PointLightCount;
float3 CB_PointLights_Padding;
PointLight PointLights[MAX_POINT_LIGHTS];
};
void ComputePointLight(inout MaterialDesc output, float3 normal, float3 wPosition)
{
output = MakeMaterial();
MaterialDesc result = MakeMaterial();
for (uint i = 0; i < PointLightCount; i++) //PointLight 개수만큼 for문을 돈다.
{
float3 light = PointLights[i].Position - wPosition;
float dist = length(light);
[flatten]
if(dist > PointLights[i].Range)
continue;
light /= dist; //Normalize
result.Ambient = PointLights[i].Ambient * Material.Ambient;
float NdotL = dot(light, normalize(normal));
float3 E = normalize(ViewPosition() - wPosition);
[flatten]
if (NdotL > 0.0f)
{
result.Diffuse = Material.Diffuse * NdotL * PointLights[i].Diffuse;
[flatten]
if (Material.Specular.a > 0.0f)
{
float3 R = normalize(reflect(-light, normal));
float RdotE = saturate(dot(R, E));
float specular = pow(RdotE, Material.Specular.a);
result.Specular = Material.Specular * specular * PointLights[i].Specular;
}
}
[flatten]
if (Material.Emissive.a > 0.0f)
{
float NdotE = dot(E, normalize(normal));
float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
result.Emissive = Material.Emissive * emissive * PointLights[i].Emissive;
}
float temp = 1.0f / saturate(dist / PointLights[i].Range);
float att = temp * temp * (1.0f / max(1.0f - PointLights[i].Intensity, 1e-8f));
output.Ambient += result.Ambient * temp;
output.Diffuse += result.Diffuse * att;
output.Specular += result.Specular * att;
output.Emissive += result.Emissive * att;
}
}
#define MAX_SPOT_LIGHTS 256
struct SpotLight
{
float4 Ambient;
float4 Diffuse;
float4 Specular;
float4 Emissive;
float3 Position;
float Range;
float3 Direction;
float Angle;
float Intensity;
float3 Padding;
};
cbuffer CB_SpotLights
{
uint SpotLightCount;
float3 CB_SpotLights_Padding;
SpotLight SpotLights[MAX_SPOT_LIGHTS];
};
void ComputeSpotLight(inout MaterialDesc output, float3 normal, float3 wPosition)
{
output = MakeMaterial();
MaterialDesc result = MakeMaterial();
for (uint i = 0; i < SpotLightCount; i++)
{
float3 light = SpotLights[i].Position - wPosition;
float dist = length(light);
[flatten]
if (dist > SpotLights[i].Range)
continue;
light /= dist; //Normalize
result.Ambient = SpotLights[i].Ambient * Material.Ambient;
float NdotL = dot(light, normalize(normal));
float3 E = normalize(ViewPosition() - wPosition);
[flatten]
if (NdotL > 0.0f)
{
result.Diffuse = Material.Diffuse * NdotL * SpotLights[i].Diffuse;
[flatten]
if (Material.Specular.a > 0.0f)
{
float3 R = normalize(reflect(-light, normal));
float RdotE = saturate(dot(R, E));
float specular = pow(RdotE, Material.Specular.a);
result.Specular = Material.Specular * specular * SpotLights[i].Specular;
}
}
[flatten]
if (Material.Emissive.a > 0.0f)
{
float NdotE = dot(E, normalize(normal));
float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
result.Emissive = Material.Emissive * emissive * SpotLights[i].Emissive;
}
float temp = pow(saturate(dot(-light, SpotLights[i].Direction)), SpotLights[i].Angle);
float att = temp * (1.0f / max(1.0f - SpotLights[i].Intensity, 1e-8f));
output.Ambient += result.Ambient * temp;
output.Diffuse += result.Diffuse * att;
output.Specular += result.Specular * att;
output.Emissive += result.Emissive * att;
}
}
void NormalMapping(float2 uv, float3 normal, float3 tangent, SamplerState samp)
{
float4 map = NormalMap.Sample(samp, uv);
[flatten]
if (any(map.rgb) == false)
return;
float3 coord = map.rgb * 2.0f - 1.0f; //-1 ~ +1
//탄젠트 공간
float3 N = normalize(normal); //Z
float3 T = normalize(tangent - dot(tangent, N) * N); //X
//float3 T = tangent;
//float3 T = float3(1, 0, 0);
float3 B = cross(N, T); //Y
float3x3 TBN = float3x3(T, B, N);
coord = mul(coord, TBN);
Material.Diffuse *= saturate(dot(-GlobalLight.Direction, coord));
}
void NormalMapping(float2 uv, float3 normal, float3 tangent)
{
NormalMapping(uv, normal, tangent, LinearSampler);
}
float4 PS_AllLight(MeshOutput input)
{
NormalMapping(input.Uv, input.Normal, input.Tangent);
Texture(Material.Diffuse, DiffuseMap, input.Uv);
Texture(Material.Specular, SpecularMap, input.Uv);
MaterialDesc output = MakeMaterial();
MaterialDesc result = MakeMaterial();
ComputeLight(output, input.Normal, input.wPosition);
AddMaterial(result, output);
ComputePointLight(output, input.Normal, input.wPosition);
AddMaterial(result, output);
ComputeSpotLight(output, input.Normal, input.wPosition);
AddMaterial(result, output);
return float4(MaterialToColor(result).rgb, 1.0f);
}
Texture2D ProjectorMap;
struct ProjectorDesc
{
matrix View;
matrix Projection;
float4 Color;
};
cbuffer CB_Projector
{
ProjectorDesc Projector;
};
void VS_Projector(inout float4 wvp, float4 oPosition)
{
wvp = WorldPosition(oPosition);
wvp = mul(wvp, Projector.View);
wvp = mul(wvp, Projector.Projection);
}
void PS_Projector(inout float4 color, float4 wvp)
{
float3 uvw = 0;
uvw.x = wvp.x / wvp.w * 0.5f + 0.5f;
uvw.y = -wvp.y / wvp.w * 0.5f + 0.5f;
uvw.z = wvp.z / wvp.w;
[flatten]
if (saturate(uvw.x) == uvw.x && saturate(uvw.y) == uvw.y && saturate(uvw.z) == uvw.z)
{
float4 map = ProjectorMap.Sample(LinearSampler, uvw.xy);
map.rgb *= Projector.Color.rgb;
color = lerp(color, map, map.a);
}
}
cbuffer CB_Shadow
{
matrix ShadowView;
matrix ShadowProjection;
float2 ShadowMapSize;
float ShadowBias;
uint ShadowQuality;
};
ComputeLight 부분 - ComputePointLight, ComputeSpotLight도 같은 원리
void ComputeLight(out MaterialDesc output, float3 normal, float3 wPosition)
{
output = MakeMaterial();
float3 direction = -GlobalLight.Direction;
float NdotL = dot(direction, normalize(normal));
output.Ambient = GlobalLight.Ambient * Material.Ambient;
float3 E = normalize(ViewPosition() - wPosition);
[flatten]
if(NdotL > 0.0f)
{
output.Diffuse = Material.Diffuse * NdotL;
[flatten]
if(Material.Specular.a > 0.0f)
{
float3 R = normalize(reflect(-direction, normal));
float RdotE = saturate(dot(R, E));
float specular = pow(RdotE, Material.Specular.a);
output.Specular = Material.Specular * specular * GlobalLight.Specular;
}
}
[flatten]
if(Material.Emissive.a > 0.0f)
{
float NdotE = dot(E, normalize(normal));
float emissive = smoothstep(1.0f - Material.Emissive.a, 1.0f, 1.0f - saturate(NdotE));
output.Emissive = Material.Emissive * emissive;
}
}
Material를 Lighting.fx에 넘겨준다.
Lighting
Lighting.fx
#include "00_Global.fx"
#include "00_Light.fx"
#include "00_Render.fx"
float4 PS(MeshOutput input) : SV_Target
{
Texture(Material.Diffuse, DiffuseMap, input.Uv);
Texture(Material.Specular, SpecularMap, input.Uv);
MaterialDesc output;
ComputeLight(output, input.Normal, input.wPosition);
return float4(MaterialToColor(output), 1.0f);
}
technique11 T0
{
P_VP(P0, VS_Mesh, PS)
P_VP(P1, VS_Model, PS)
P_VP(P2, VS_Animation, PS)
}
LightingDemo
LightingDemo.h
#pragma once
#include "Systems/IExecute.h"
class LightingDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {}
virtual void Destroy() override {}
virtual void Update() override;
virtual void PreRender() override {}
virtual void Render() override;
virtual void PostRender() override {}
virtual void ResizeScreen() override {}
private:
void Mesh();
void Airplane();
void Kachujin();
void KachujinCollider();
void KachujinWeapon();
void Pass(UINT mesh, UINT model, UINT anim);
private:
Shader* shader;
CubeSky* sky;
Material* floor;
Material* stone;
Material* brick;
Material* wall;
MeshRender* cube;
MeshRender* cylinder;
MeshRender* sphere;
MeshRender* grid;
ModelRender* airplane = NULL;
ModelAnimator* kachujin = NULL;
Transform* colliderInitTransforms;
ColliderObject** colliders;
ModelRender* weapon = NULL;
Transform* weaponInitTransform;
vector<MeshRender *> meshes;
vector<ModelRender *> models;
vector<ModelAnimator *> animators;
};
LightingDemo.cpp
#include "stdafx.h"
#include "LightingDemo.h"
void LightingDemo::Initialize()
{
Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
Context::Get()->GetCamera()->Position(1, 36, -85);
//Performance perfomence;
//perfomence.Start();
//{
shader = new Shader(L"75_Lighting.fxo");
//}
//float t = perfomence.End();
//MessageBox(D3D::GetDesc().Handle, to_wstring(t).c_str(), L"", MB_OK);
sky = new CubeSky(L"Environment/GrassCube1024.dds");
Mesh();
Airplane();
Kachujin();
KachujinCollider();
KachujinWeapon();
}
void LightingDemo::Update()
{
ImGui::ColorEdit3("Ambient", Context::Get()->Ambient());
ImGui::ColorEdit3("Specular", Context::Get()->Specular());
sky->Update();
cube->Update();
grid->Update();
cylinder->Update();
sphere->Update();
airplane->Update();
kachujin->Update();
Matrix worlds[MAX_MODEL_TRANSFORMS];
for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
{
kachujin->GetAttachTransform(i, worlds);
//colliders[i]->Collider->GetTransform()->World(worlds[40]);
//colliders[i]->Collider->Update();
weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]);
}
weapon->UpdateTransforms();
weapon->Update();
}
void LightingDemo::Render()
{
sky->Render();
Pass(0, 1, 2);
wall->Render();
sphere->Render();
brick->Render();
cylinder->Render();
stone->Render();
cube->Render();
floor->Render();
grid->Render();
airplane->Render();
kachujin->Render();
weapon->Render();
//for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
//colliders[i]->Collider->Render();
}
void LightingDemo::Mesh()
{
//Create Material
{
floor = new Material(shader);
floor->DiffuseMap("Floor.png");
floor->Ambient(0.2f, 0.2f, 0.2f, 20);
floor->Specular(1, 1, 1, 20);
floor->SpecularMap("Floor_Specular.png");
//floor->NormalMap("Floor_Normal.png");
stone = new Material(shader);
stone->DiffuseMap("Stones.png");
stone->Ambient(0.2f, 0.2f, 0.2f, 20);
stone->Specular(1, 1, 1, 20);
stone->SpecularMap("Stones_Specular.png");
stone->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
//stone->NormalMap("Stones_Normal.png");
brick = new Material(shader);
brick->DiffuseMap("Bricks.png");
brick->Ambient(0.2f, 0.2f, 0.2f, 20);
brick->Specular(1, 0.3f, 0.3f, 20);
brick->SpecularMap("Bricks_Specular.png");
brick->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
//brick->NormalMap("Bricks_Normal.png");
wall = new Material(shader);
wall->DiffuseMap("Wall.png");
wall->Ambient(0.2f, 0.2f, 0.2f, 20);
wall->Specular(1, 1, 1, 20);
wall->SpecularMap("Wall_Specular.png");
wall->Emissive(0.15f, 0.15f, 0.15f, 0.3f);
//wall->NormalMap("Wall_Normal.png");
}
//Create Mesh
{
Transform* transform = NULL;
cube = new MeshRender(shader, new MeshCube());
transform = cube->AddTransform();
transform->Position(0, 5, 0);
transform->Scale(20, 10, 20);
grid = new MeshRender(shader, new MeshGrid(5, 5));
transform = grid->AddTransform();
transform->Position(0, 0, 0);
transform->Scale(12, 1, 12);
cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
for (UINT i = 0; i < 5; i++)
{
transform = cylinder->AddTransform();
transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = cylinder->AddTransform();
transform->Position(30, 6, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = sphere->AddTransform();
transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
transform = sphere->AddTransform();
transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
transform->Scale(5, 5, 5);
}
}
sphere->UpdateTransforms();
cylinder->UpdateTransforms();
cube->UpdateTransforms();
grid->UpdateTransforms();
meshes.push_back(sphere);
meshes.push_back(cylinder);
meshes.push_back(cube);
meshes.push_back(grid);
}
void LightingDemo::Airplane()
{
airplane = new ModelRender(shader);
airplane->ReadMesh(L"B787/Airplane");
airplane->ReadMaterial(L"B787/Airplane");
Transform* transform = airplane->AddTransform();
transform->Position(2.0f, 9.91f, 2.0f);
transform->Scale(0.004f, 0.004f, 0.004f);
airplane->UpdateTransforms();
models.push_back(airplane);
}
void LightingDemo::Kachujin()
{
kachujin = new ModelAnimator(shader);
kachujin->ReadMesh(L"Kachujin/Mesh");
kachujin->ReadMaterial(L"Kachujin/Mesh");
kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
kachujin->ReadClip(L"Kachujin/Salsa Dancing");
Transform* transform = NULL;
transform = kachujin->AddTransform();
transform->Position(0, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(0, 0, 1.0f);
transform = kachujin->AddTransform();
transform->Position(-15, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(1, 1, 1.0f);
transform = kachujin->AddTransform();
transform->Position(-30, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(2, 2, 0.75f);
transform = kachujin->AddTransform();
transform->Position(15, 0, -30);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayBlendMode(3, 0, 1, 2);
kachujin->SetBlendAlpha(3, 1.5f);
transform = kachujin->AddTransform();
transform->Position(30, 0, -32.5f);
transform->Scale(0.075f, 0.075f, 0.075f);
kachujin->PlayTweenMode(4, 4, 0.75f);
kachujin->UpdateTransforms();
animators.push_back(kachujin);
}
void LightingDemo::KachujinCollider()
{
UINT count = kachujin->GetTransformCount();
colliders = new ColliderObject*[count];
colliderInitTransforms = new Transform();
colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f);
colliderInitTransforms->Scale(5, 5, 75);
for (UINT i = 0; i < count; i++)
{
colliders[i] = new ColliderObject();
//colliders[i]->Init = new Transform();
//colliders[i]->Init->Position(0, 0, 0);
//colliders[i]->Init->Scale(10, 30, 10);
colliders[i]->Transform = new Transform();
//colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms);
}
}
void LightingDemo::KachujinWeapon()
{
weapon = new ModelRender(shader);
weapon->ReadMesh(L"Weapon/Sword");
weapon->ReadMaterial(L"Weapon/Sword");
UINT count = kachujin->GetTransformCount();
for (UINT i = 0; i < count; i++)
weapon->AddTransform();
weapon->UpdateTransforms();
models.push_back(weapon);
weaponInitTransform = new Transform();
weaponInitTransform->Position(-2.9f, 1.45f, -6.45f);
weaponInitTransform->Scale(0.5f, 0.5f, 0.75f);
weaponInitTransform->Rotation(0, 0, 1);
}
void LightingDemo::Pass(UINT mesh, UINT model, UINT anim)
{
for (MeshRender* temp : meshes)
temp->Pass(mesh);
for (ModelRender* temp : models)
temp->Pass(model);
for (ModelAnimator* temp : animators)
temp->Pass(anim);
}
실행화면
Specular를 조정한 결과
윤곽선 효과를 적용한 결과
'⭐ DirectX > DirectX11 3D' 카테고리의 다른 글
[DirectX11] 080 Spot Lighting (0) | 2023.03.26 |
---|---|
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch] (0) | 2023.03.21 |
[DirectX11] 074 Instancing (0) | 2023.03.18 |
[DirectX11] 071~73 Get MultiBones (0) | 2023.03.15 |
[DirectX11] 070 OBB(Oriented Bounding Box) collision (3) | 2023.03.14 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 080 Spot Lighting
[DirectX11] 080 Spot Lighting
2023.03.26 -
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch]
[DirectX11] 078~79 Point Lighting, HLSL [flatten] [branch]
2023.03.21 -
[DirectX11] 074 Instancing
[DirectX11] 074 Instancing
2023.03.18 -
[DirectX11] 071~73 Get MultiBones
[DirectX11] 071~73 Get MultiBones
2023.03.15