[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 (4) | 2023.03.14 |
댓글
이 글 공유하기
다른 글
-
[DirectX11] 080 Spot Lighting
[DirectX11] 080 Spot Lighting
2023.03.26DirectX 11에서 Spotlight를 만들기. Spotlight용 셰이더를 작성한다. 먼저, 셰이더 상수 버퍼를 정의한다. ObjectTransforms 상수 버퍼는 개체의 변환 행렬을 저장하고 SpotLightBuffer 상수 버퍼는 스포트 라이트의 위치, 방향, 색상 및 라이트의 스포트 파라미터를 저장한다. Spotlight 셰이더의 VSMain 함수는 입력 버텍스의 위치와 법선을 투영 공간으로 변환하고 광원 공간으로 변환한다. 목차 Spot Lighting DirectX 11에서 Spotlight를 만들기 Spotlight용 셰이더를 작성한다. 먼저, 셰이더 상수 버퍼를 정의한다. ObjectTransforms 상수 버퍼는 개체의 변환 행렬을 저장하고 SpotLightBuffer 상수 버퍼는 … -
[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
댓글을 사용할 수 없습니다.