3Dの光源
papas  2016/05/30(Mon) 15:10
懐中電灯のような光源を作りたいのですが、どのように書けばよいですか?
画像のような光で、自由に大きさを調整できるようにしたいです。



記事編集
Reputeless  2016/05/31(Tue) 18:36
Siv3D の基本光源にスポッライトは無いので、
3D 描画を Forward Rendering にして、カスタムシェーダでスポットライト光源を処理してください。

[SpotLight.hlsl]
https://gist.github.com/Reputeless/840382fde6ba5a18e216a18dfef22f75

[Main]
# include <Siv3D.hpp>

struct SpotLight
{
Float3 position;
float attenuation;
Float3 diffuseColor;
float exponent;
Float3 direction;
float cutoff;
};

void Main()
{
const Texture textureGround(L"Example/Ground.jpg", TextureDesc::For3D);
const Texture textureBrick(L"Example/Brick.jpg", TextureDesc::For3D);

Graphics::SetBackground(ColorF(0.02, 0.02, 0.05));
Graphics3D::SetLightForward(0, Light::None());
Graphics3D::SetAmbientLightForward(ColorF(0.05));

// SpotLight
const VertexShader vs(L"SpotLight.hlsl");
const PixelShader ps(L"SpotLight.hlsl");
ConstantBuffer<SpotLight> cb;
if (!vs || !ps)
return;

GUI gui(GUIStyle::Default);
gui.add(GUIText::Create(L"attenuation"));
gui.addln(L"attenuation", GUISlider::Create(0.0, 0.1, 0.01));
gui.add(GUIText::Create(L"exponent"));
gui.addln(L"exponent", GUISlider::Create(0.0, 256.0, 32.0));
gui.add(GUIText::Create(L"cutoff"));
gui.addln(L"cutoff", GUISlider::Create(0_deg, 90_deg, 30_deg));

while (System::Update())
{
Graphics3D::FreeCamera();

cb->position = Graphics3D::GetCamera().pos + Vec3(1, 0, 0);// Vec3(20, 20, -20);
cb->attenuation = gui.slider(L"attenuation").value;
cb->diffuseColor = ColorF(1, 0.9, 0.3).rgb();
cb->exponent = gui.slider(L"exponent").value;
cb->direction = Mouse::Ray().direction;
cb->cutoff = gui.slider(L"cutoff").value;

Graphics3D::SetConstantForward(ShaderStage::Pixel, 1, cb);
Graphics3D::BeginVSForward(vs);
Graphics3D::BeginPSForward(ps);

Plane(1000).drawForward(textureGround);

for (int32 x = -5; x <= 5; ++x)
{
Box(x * 8, 5, 10, 4, 10, 2).drawForward(textureBrick);
}

Graphics3D::EndVSForward();
Graphics3D::EndPSForward();
}
}



編集
papas  2016/06/06(Mon) 14:03
ありがとうございます
うまくいきました
編集
とし  2025/04/28(Mon) 15:38
これって昔のSiv3Dのサンプルですか?
OpenSiv3D_0.6.16でビルドしてみたんですけどエラーがいっぱいでます。
今の元openSiv3Dのサンプルはありませんか?
後directional lightに影がついたので(大感謝!サンプルを勉強中です、、、)スポットライトの影のつけ方も知りたいです
編集
Reputeless  2025/04/30(Wed) 13:56
現在のバージョン用のサンプルを作ってみます。
編集
Reputeless  2025/05/05(Mon) 02:00
複数のスポットライト光源のサンプルです。
https://gist.github.com/Reputeless/9064220b9fc72caf1e3c07acf3d09da6

スポットライトのシャドウは、現バージョンでは難しいです。
編集
とし  2025/05/05(Mon) 16:01
早速のお返事ありがとうございます!
分からなかったところががんがん分かって目から鱗が落ちてます!

ところで光関係でもう一つ質問なのですが、
https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-3d-2
37.7 3D カスタムシェーダ
でdefault3d_forward.vert、default3d_forward.fragを使用するサンプルがありますが、
シーンを作った時にディレクショナルライトがある訳じゃないですか、
その後、GLSLをロードして演算したらディレクショナルライトの計算を二回することになりませんか?


デフォルトで入ってるディレクショナルライトの計算をキャンセルする必要はないのですか?
編集
Reputeless  2025/05/05(Mon) 22:35
標準シェーダの代わりに、標準シェーダと同内容の default3d_forward.vert / default3d_forward.frag を使うだけなので、何も変わりません。
編集
とし  2025/05/08(Thu) 17:08
const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };
この処理の時にDirectional Lightも生成されていると言うことであっていますか?
編集
Reputeless  2025/05/08(Thu) 23:03
3D の `.draw()` 時、Directional Light (Siv3D の呼び方では SunColor)は常に有効化されていて、標準シェーダで計算されています。
生成や破棄はありません。

https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-3d#36.18-%E5%A4%AA%E9%99%BD%E5%85%89%E3%81%AE%E8%89%B2%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B
編集
とし  2025/05/27(Tue) 09:36
.drawの時点なんですね!
ありがとうございます、やっとソースコードのどこを見ればいいのかわかりました!

こちらにポイントライトのサンプルがあるのですが、
https://gist.github.com/Reputeless/eb4830bb2efb6f8734478945008f8b42
これのGLSLバージョンってありませんか?
編集
Reputeless  2025/05/28(Wed) 13:04
とし  2025/05/30(Fri) 13:49
自分で移植してみました!

main.cpp///////////////////////////////////////////////////////////////////////////////////////
# include <Siv3D.hpp>
SIV3D_SET(EngineOption::Renderer::OpenGL);

struct PSLighting
{
static constexpr uint32 MaxPointLights = 4;

struct Light
{
Float4 position{ 0, 0, 0, 0 };
Float4 diffuseColor{ 0, 0, 0, 0 };
Float4 attenuation{ 1.0f, 2.0f, 1.0f, 0 };
};

std::array<Light, MaxPointLights> pointLights;

/// @brief 点光源を設定します。
/// @param i 光源のインデックス。0 以上 MaxPointLights 未満である必要があります。
/// @param pos 光源の位置
/// @param diffuse 色
/// @param r 強さ
void setPointLight(uint32 i, const Vec3& pos, const ColorF& diffuse, double r)
{
pointLights[i].position = Float4{ pos, 1.0f };
pointLights[i].diffuseColor = diffuse.toFloat4();
pointLights[i].attenuation = Float4{ 1.0, (2.0 / r), (1.0 / (r * r)), 0.0 };
}

/// @brief 点光源を球として描画します。
/// @param i 光源のインデックス。0 以上 MaxPointLights 未満である必要があります。
/// @param r 球の半径
void drawPointLightAsEmissiveSphere(uint32 i, double r)
{
const Vec3 pos = pointLights[i].position.xyz();
const ColorF diffuse{ pointLights[i].diffuseColor };

PhongMaterial phong;
phong.ambientColor = ColorF{ 0.0 };
phong.diffuseColor = ColorF{ 0.0 };
phong.emissionColor = diffuse;
Sphere{ pos, r }.draw(phong);
}
};

void Main()
{
Window::Resize(1280, 720);
const ColorF backgroundColor = ColorF{ 0.2, 0.2, 0.2 }.removeSRGBCurve();
const Texture uvChecker{ U"example/texture/uv.png", TextureDesc::MippedSRGB };
const MSRenderTexture renderTexture{ Scene::Size(), TextureFormat::R8G8B8A8_Unorm_SRGB, HasDepth::Yes };
DebugCamera3D camera{ renderTexture.size(), 30_deg, Vec3{ 10, 16, -32 } };

// カスタムピクセルシェーダ
const PixelShader ps3D = HLSL{ U"example/shader/hlsl/point_light.hlsl", U"PS" } | GLSL{ U"example/shader/glsl/point_light.frag", {{ U"PSPerFrame", 0 }, { U"PSPerView", 1 }, { U"PSPerMaterial", 3 }, { U"PSLighting", 4 }} };;
ConstantBuffer<PSLighting> constantBuffer;
if (not ps3D)
{
throw Error{ U"Failed to load shader files" };
}

while (System::Update())
{
camera.update(2.0);
Graphics3D::SetCameraTransform(camera);

// 環境光を小さくする
Graphics3D::SetGlobalAmbientColor(ColorF{ 0.01 });
// 太陽光をオフにする
Graphics3D::SetSunColor(ColorF{ 0.0 });
// 点光源を設定する
constantBuffer->setPointLight(0, Vec3{ -8, 2, -8 }, ColorF{ 1.0, 0.2, 0.0 }, 5.0);
constantBuffer->setPointLight(1, Cylindrical{ 12, (Scene::Time() * 30_deg), (0.5 + Periodic::Sine0_1(2s) * 2) }, ColorF{ 0.2, 1.0, 0.2 }, 5.0);
constantBuffer->setPointLight(2, Vec3{ 8, 2, -8 }, ColorF{ 0.2, 0.5, 1.0 }, 5.0);

{
// カスタムシェーダを使用する
const ScopedCustomShader3D shader{ ps3D };
// ピクセルシェーダに定数バッファを渡す
Graphics3D::SetPSConstantBuffer(4, constantBuffer);

const ScopedRenderTarget3D target{ renderTexture.clear(backgroundColor) };
Plane{ 64 }.draw(uvChecker);
Box{ -8,2,0,4 }.draw(ColorF{ 0.8, 0.6, 0.4 }.removeSRGBCurve());
Sphere{ 0,2,0,2 }.draw(ColorF{ 0.4, 0.8, 0.6 }.removeSRGBCurve());
Cylinder{ 8, 2, 0, 2, 4 }.draw(ColorF{ 0.6, 0.4, 0.8 }.removeSRGBCurve());

constantBuffer->drawPointLightAsEmissiveSphere(0, 0.2);
constantBuffer->drawPointLightAsEmissiveSphere(1, 0.2);
constantBuffer->drawPointLightAsEmissiveSphere(2, 0.2);
}

{
Graphics3D::Flush();
renderTexture.resolve();
Shader::LinearToScreen(renderTexture);
}
}
}

point_light.frag////////////////////////////////////////////////////////////////////////
// Copyright (c) 2008-2025 Ryo Suzuki.
// Copyright (c) 2016-2025 OpenSiv3D Project.
// Licensed under the MIT License.

# version 410

//
// Textures
//
uniform sampler2D Texture0;

struct Light
{
vec4 position;
vec4 diffuseColor;
vec4 attenuation;
};

//
// PSInput
//
layout(location = 0) in vec3 WorldPosition;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec3 Normal;

//
// PSOutput
//
layout(location = 0) out vec4 FragColor;

//
// Constant Buffer
//
layout(std140) uniform PSPerFrame // slot 0
{
vec3 g_globalAmbientColor;
vec3 g_sunColor;
vec3 g_sunDirection;
};

layout(std140) uniform PSPerView // slot 1
{
vec3 g_eyePosition;
};

layout(std140) uniform PSPerMaterial // slot 3
{
vec3 g_ambientColor;
uint g_hasTexture;
vec4 g_diffuseColor;
vec3 g_specularColor;
float g_shininess;
vec3 g_emissionColor;
};

const uint MaxPointLights = 4;
layout(std140) uniform PSLighting // slot 4
{
Light g_lights[MaxPointLights];
};

vec3 CalculatePointLight(uint index, vec3 surfaceNormal, vec3 surfacePosition) {
Light light = g_lights[index];
vec3 lightPosition = light.position.xyz;
vec3 lightDirection = lightPosition - surfacePosition;
float d = length(lightDirection);
float Kc = light.attenuation.x;
float Kl = light.attenuation.y;
float Kq = light.attenuation.z;
float f_att = 1.0 / (Kc + Kl * d + Kq * d * d);
lightDirection = normalize(lightDirection);
float diffuseInfluence = max(dot(lightDirection, surfaceNormal), 0.0f) * f_att;
return light.diffuseColor.rgb * diffuseInfluence;
}

vec4 GetDiffuseColor(vec2 uv) {
vec4 diffuseColor = g_diffuseColor;
if (g_hasTexture == 1u) {
diffuseColor *= texture(Texture0, uv);
}
return diffuseColor;
}

vec3 CalculateDiffuseReflection(vec3 n, vec3 l, vec3 lightColor, vec3 diffuseColor, vec3 ambientColor) {
vec3 directColor = lightColor * max(dot(n, l), 0.0f);
return (ambientColor + directColor) * diffuseColor;
}

vec3 CalculateSpecularReflection(vec3 n, vec3 h, float shininess, float nl, vec3 lightColor, vec3 specularColor) {
float highlight = pow(max(dot(n, h), 0.0f), shininess) * float(nl > 0.0);
return lightColor * specularColor * highlight;
}

void main() {
vec3 lightColor = g_sunColor;
vec3 lightDirection = g_sunDirection;

vec3 n = normalize(Normal);
vec3 l = lightDirection;
vec4 diffuseColor = GetDiffuseColor(UV);
vec3 ambientColor = g_ambientColor * g_globalAmbientColor;

vec3 diffuseReflection = CalculateDiffuseReflection(n, l, lightColor, diffuseColor.rgb, ambientColor);

for (uint i = 0u; i < MaxPointLights; ++i) { // 修正済み
diffuseReflection += diffuseColor.rgb * CalculatePointLight(i, n, WorldPosition);
}

vec3 v = normalize(g_eyePosition - WorldPosition);
vec3 h = normalize(v + lightDirection);
vec3 specularReflection = CalculateSpecularReflection(n, h, g_shininess, dot(n, l), lightColor, g_specularColor);

FragColor = vec4(diffuseReflection + specularReflection + g_emissionColor, diffuseColor.a);
}
////////////////////////////////////////////////////////////////////////////////

hlslと同じように動いてるようなのですけど、問題ありませんかね?
siv3dみたいに綺麗なコードで書けてたら良いのですけど、、、
編集
Reputeless  2025/05/30(Fri) 14:14
コードが長いのですべてのチェックはできませんが、動作しているのであれば問題ないと思います。
編集
件名
Re: 3Dの光源
名前
コメント
画像添付


投稿修正キー (投稿を修正する時に使います)
画像認証 (右画像の数字を入力) 投稿キー

- WEB PATIO -