BlenderでSDFベースのモデリングを行うアドオンを作成しました.3か月前にGithubに最後のコミットを行ってから暫くの間ソースの改修を行えていませんが,記憶が新しい内にSDFベースのモデリングという概念やアドオンで実現したかったこと,また,実装した機能や現在の課題について,宣伝がてらGithubだけでなくこの記事にもまとめようとおもいます.
ソースコードはこちら
(ここは最後に書く)
SDF(符号付距離関数)を駆使して,物体の形状を定義(モデリング)していくモデリングのアプローチです.数式で物体の形を定義するということです.
例えば球体は,以下のように表現することができます.
// 球体の距離関数
float sdSphere( vec3 p, float s )
{
return length(p)-s;
}
定義した関数
sdSphere
について説明します.引数s
は球体の半径で,引数p
は半径s
,位置(0,0,0)
の球体に対して,球体の表面からの距離を測りたい座標です.関数sdSphere
はある球体の表面と,ある座標との距離を符号付きで返します(引数p
が球体の内側にあれば,戻り値の符号は-
,外側にあれば,戻り値の符号は+
です).p
に(1,0,0)
,引数s
に0.5
を与えると,関数sdSphere
は,半径0.5
,位置(0,0,0)
に位置する球体の表面と,座標(1,0,0)
との距離を測ることとなり,0.5
を返します.また,引数p
に(0,0,0)
を与えると,関数sdSphere
は-0.5
を返します.
このように,ある物体に対して,物体の表面からの距離を,物体の表面との内外判定を考慮して計算する関数をSDF(符号付距離関数)と呼びます.ここからは,SDFで物体と任意の座標との距離を内外判定付きで計算することが出来たとして,それをどうモデリングに活かせるのかについて説明します.
その前に,モデリングって何でしょうか?wiki等で調べれば厳密な定義を知ることが出来るかもしれませんが,難しそうなのでやっぱり触れません.その代わり,特にコンピュータグラフィックスにおいて,モデリングという作業のゴールについて自分の解釈を整理してみようと思います.
自分の解釈では,コンピュータグラフィックスにおける物体のモデリングという作業のゴールは大きく2つ,1. 物体を画像や映像に出力すること,2. 物体をメッシュとして出力することが挙げられると考えています.正確には全て1に集約されるのかもしれませんが,2は例として,物体を.obj
等に変換し,UnityやBlenderのようなソフトで汎用的に使用できるようにすることであり,1は物体をメッシュとして扱うことに拘らず,とにかく何かしらのソフト上で物体を画像や映像に出力することができればOKという場面・目的になります.なので2つに分けてみました.前章で説明した,SDFをモデリングに活かすという考えは,SDFを活用して,上記の2つのゴールを達成するということと言い換えることができます.
はじめに,SDFを利用して,前章で整理したモデリングにおける目的の1つ目(物体を画像や映像に出力する)を実現するまでの流れを辿っていきましょう.HLSLやGLSL等,コンピュータグラフィックスにおける代表的なシェーダー言語においては,レイマーチングという手法をもってこれを実現できます.例えば,GLSLで以下のようなレイマーチングシェーダーを書いてみます(shadertoyで実行することを想定).
// The MIT License
// Copyright © 2020 Inigo Quilez
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Exact euclidean distance to a box frame
// List of other 3D SDFs:
// https://www.shadertoy.com/playlist/43cXRl
// and
// https://iquilezles.org/articles/distfunctions
//===========================================================================
// @ 2025 TLabAltoh
// Inigo Quilez氏の公開したシェーダを基盤に作成したサンプルです.ソースコードの権利については,上記を参照してください
//===========================================================================
float sdSphere( vec3 p, float s )
{
return length(p)-s;
}
//===========================================================================
float map( in vec3 pos )
{
return sdSphere(pos, 0.5 );
}
// https://iquilezles.org/articles/normalsSDF
vec3 calcNormal( in vec3 pos )
{
vec2 e = vec2(1.0,-1.0)*0.5773;
const float eps = 0.0005;
return normalize( e.xyy*map( pos + e.xyy*eps ) +
e.yyx*map( pos + e.yyx*eps ) +
e.yxy*map( pos + e.yxy*eps ) +
e.xxx*map( pos + e.xxx*eps ) );
}
float raycast( in vec3 ro, in vec3 rd, float tmax )
{
float t = 0.0;
for( int i=0; i<256; i++ )
{
vec3 pos = ro + t*rd;
float h = map(pos);
if( h<0.0001 || t>tmax ) break;
t += h;
}
return (t<tmax)?t:-1.0;
}
vec3 lighting( vec3 nor, vec3 mate )
{
vec3 lig = vec3(0.0);
float amb = 0.5 + 0.5*nor.y;
lig += mate*vec3(0.2,0.3,0.4)*amb;
float dif = clamp( dot(nor,vec3(0.57703)), 0.0, 1.0 );
lig += mate*vec3(0.85,0.75,0.65)*dif;
return lig;
}
mat4x4 setCameraToWorld( in vec3 ro, in vec3 ta, float cr )
{
vec3 cw = normalize(ro-ta);
vec3 cp = vec3(0.0, cos(cr),sin(cr));
vec3 cu = normalize(cross(cp,cw));
vec3 cv = (cross(cw,cu));
return mat4x4( cu.x, cu.y, cu.z, 0.0, // note transpose notation because of GLSL row major
cv.x, cv.y, cv.z, 0.0,
cw.x, cw.y, cw.z, 0.0,
ro.x, ro.y, ro.z, 1.0 );
}
mat4x4 setWorldToCamera(in vec3 ro, in vec3 ta, float cr )
{
vec3 cw = normalize(ro-ta);
vec3 cp = vec3(0.0, cos(cr),sin(cr));
vec3 cu = normalize(cross(cp,cw));
vec3 cv = (cross(cw,cu));
return mat4x4( cu.x, cv.x, cw.x, 0.0,
cu.y, cv.y, cw.y, 0.0,
cu.z, cv.z, cw.z, 0.0,
-dot(cu,ro), -dot(cv,ro), -dot(cw,ro), 1.0 );
}
#if HW_PERFORMANCE==0
#define AA 1
#else
#define AA 3
#endif
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// camera movement
float an = 0.15*(iTime-10.0);
vec3 ro = 1.2*vec3( 1.0*cos(an), 0.0, 1.0*sin(an) );
vec3 ta = vec3( 0.0, -0.0, 0.0 );
// camera matrix
#if 1
mat4x4 cameraToWorld = setCameraToWorld( ro, ta, 0.0 );
mat4x4 worldToCamera = inverse(cameraToWorld);
#else
mat4x4 worldToCamera = setWorldToCamera( ro, ta, 0.0 );
mat4x4 cameraToWorld = inverse(worldToCamera);
#endif
// camera projection
const float fle = 1.5;
// probe
vec2 m = (2.0*iMouse.xy-iResolution.xy)/iResolution.y;
vec3 mrd = normalize(cameraToWorld*vec4(m,-fle,0.0)).xyz;
float t2 = (-0.4-ro.y)/mrd.y;
// render
vec3 tot = vec3(0.0);
#if AA>1
for( int m=0; m<AA; m++ )
for( int n=0; n<AA; n++ )
{
// pixel coordinates
vec2 o = vec2(float(m),float(n)) / float(AA) - 0.5;
vec2 p = (2.0*(fragCoord+o)-iResolution.xy)/iResolution.y;
#else
vec2 p = (2.0*fragCoord-iResolution.xy)/iResolution.y;
#endif
float px = 2.0/iResolution.y;
// create view ray
vec3 rd = normalize(cameraToWorld*vec4(p,-fle,0.0)).xyz;
vec3 col = vec3(0.0);
float tmin = 1e20;
// SDF shape
{
float t = raycast(ro,rd,3.0);
if( t>0.0 )
{
tmin = t;
vec3 pos = ro + t*rd;
vec3 nor = calcNormal(pos);
col = lighting( nor, vec3(1.0) );
}
}
// gamma
col = sqrt( col );
tot += col;
#if AA>1
}
tot /= float(AA*AA);
#endif
fragColor = vec4( tot, 1.0 );
}
上記シェーダーをshadertoyで実行すると,以下の結果を得ることが出来ます

Blender上で,SDFで定義していたトーラスをメッシュに変換することができました!ここまでで,前章で整理したコンピュータグラフィクスにおけるモデリングの目的1,2をSDFを活用することで達成することが確認できました.SDFベースのモデリングという概念は,ここまでで検証したSDFを活用する物体の描画,メッシュ化というプロセスに則ってコンピュータグラフィクスにおけるモデリングを行うことにあたります.また,今回作成したアドオンは,この,SDFベースのモデリングをBlender上で実現することを目的としたものになります.以下に作成したアドオンの機能や現状の課題・制約をまとめていきます.【進捗】前回はレイマーチングまでやったので,今回はレイマーチング→メッシュに変換するところまで📝
— TLabAltoh (@TLab22913171) March 21, 2025
メッシュへの変換はマーチングキューブ (頂点補間あり) をコンピュートシェーダー上で計算して行っています
かなり開発がゆっくりだけど最後まで頑張る ...#blender #glsl https://t.co/uoxnFO0xWx pic.twitter.com/4mPXpzxkse
(これから書く)