質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

Q&A

解決済

2回答

1793閲覧

GLSLのループについて

chevy

総合スコア2

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

GLSL

GLSL (OpenGL Shading Language) はC言語をベースとしたシェーディング言語です。

0グッド

0クリップ

投稿2021/07/08 09:31

GLSL初心者です。
フラグメントシェーダー というものが、描画領域を1pxずつ処理する、ということは
理解できるのですが、以下のコードで、白いグラデーションの玉が2つ描画される理由がよくわかりません。
結局gl_FragColorにセットされるのはひとつのvec4なのに、
なぜループすると2つの玉が描画されるのでしょうか....。

GLSL

1precision mediump float; 2uniform vec2 r; // resolution 3 4void main(void){ 5 vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); 6 vec3 destColor = vec3(0.0); 7 for(float i = 0.0; i < 2.0; i++){ 8 float j = i + 1.0; 9 vec2 q = p + vec2(cos(j), sin(j)) * 0.5; 10 destColor += 0.01 / length(q); 11 } 12 gl_FragColor = vec4(destColor, 1.0); 13}

わかりやすい説明をお願いしたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

ご自身で書いたコードの説明を他者に求めている状況が謎すぎますが……


2つの玉が描画される

の理由に関して,
コードの雰囲気を見て注釈を書いてみるならば…(↓のコードに★で始まる注釈を記した)

void main(void){ vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y); vec3 destColor = vec3(0.0); for(float i = 0.0; i < 2.0; i++){ //★2回ループする float j = i + 1.0; //★このjは2回のループの各回で異なる値になるのだから… vec2 q = p + vec2(cos(j), sin(j)) * 0.5; //★このqは2回のループの各回で違う場所を示す destColor += 0.01 / length(q); //★ここの右辺は,qが(0,0)に近い場合に急激に明るくなる } gl_FragColor = vec4(destColor, 1.0); }

こういう感じ.

ある画素位置(A) について,ループで2回計算されるqのうちの一方が非常に(0,0)に近いとしよう.
すると,その画素の色は結果として非常に明るくなるだろう.
(このqだけで destColor の値は本当に大きな値になるだろうから,このときの他方のqの影響はどうでもいいレベルだ)

さて,ここで,
【この「非常に(0,0)に近いq」の座標が色の決定に用いられるのは,この画素位置(A)だけであろうか?】
ということを考えてみよう.

1つの画素位置について,qは2か所計算されるのだから,
例えば,画素位置(A)において明るい結果をもたらしたqというのが「1個目」の側だとしたら,
このqと全く同じ座標を「2個目」として得るような他の画素位置(B)が存在し得るであろう.

だから,同程度にすっごい明るい画素が2か所現れることになる.

その2か所とはどれだけ離れているのだろうか? というのは,(めんどくせぇからいちいち説明しないけども)コードを見る感じ「相応に離れている」箇所となるだろう.

以上が,2つの玉が描画される理由であると推測される.

投稿2021/07/09 01:46

fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

chevy

2021/07/09 06:16

(自分で書いたソースコードですが、色々なサンプルをコピペしつつ試したものでした) 説明、すごくよく理解できました。 まず混乱していたのが、 ループ内の座標とgl_FragCoordの座標についてです。 ・描画するピクセルは、(当然のことながら)gl_FragCoordが指定する座標のみ。 ・ループ内の座標(vec2で計算しているもの)は、 0.01 / length(q); この計算をするためのもので、描画するピクセル位置とは無関係。 つまり、 正規化されたpと加算(=q)することで「0,0との近さ」の幅を作るためのもの。 なぜループするほど白い玉の数が増えるか? については、 ・pは正規化されているので、常に、x=[-1から1]、y=-[-1から1]の値。 ・jは常に1.0 または 2.0。なので、 vec2(cos(j), sin(j)) これは、常に[0.540....,0.841...] または[-0.416...,0.909...]。 これに0.5を常に乗算しているので、これらの半分の値、つまり [0.270...,0.420...]、[-0.208...,0.454....]を、正規化された描画ピクセルの座標(p)に加算している。 GLSLだとデバッグができないので、JSで同じようなコードを作って検証してみました。 ``` const r = 512 for(let i1 = 0;i1<=512;i1++){ for(let i2=0;i2<=512;i2++){ const coord = [i1,i2] let col = 0 for(let z=0.0;z<2.0;z++){ const j = z+1 //正規化、px、pyは-1〜1に収まる const px = (coord[0] * 2.0 - r) / r const py = (coord[1] * 2.0 - r) / r const qx = px+(Math.cos(j)*0.5)//0.5..か-0.4...くらい const qy = py+(Math.sin(j)*0.5)//0.8..か0.4...くらい //原点0,0との距離が0.01に近いほど、明るくなる。 const len = Math.sqrt( Math.pow( qx, 2 ) + Math.pow( qy, 2 )) col += 0.01/len } if(col >= 1){ console.log('x=' + i1 +",y=" + i2 ); } } } ``` まっしろになる=colが1以上になる座標を出力してみると、 x=185〜189のときy=147〜149、 x=307〜311のときy=139〜141 これらの場合にcol>=1になることがわかりました。 これは、ラジアン が1ごとに57.295...度の角度になるため、 それに応じてqx、qyが定まり、ループ回数を1増やすたびに ラジアン 1度分の座標が、0.01/lenの計算により1に近づく機会が増える.... ということだと理解しました。 説明がうまくないので伝わりにくいかもですが、 実際の値を見つつ考えたら、当然こうなるな、と理解できました。 ありがとうございました。
chevy

2021/07/09 06:19

※上記ソースコード、解像度512x512で想定したものです。
fana

2021/07/09 06:44

うん.この演算だと,正規化した世界の中で, ・0[rad]の方向に 0.5 だけ進んだ位置が (0,0) に非常に近くなるような場所 ・1[rad]の方向に 0.5 だけ進んだ位置が (0,0) に非常に近くなるような場所 のどちらも白くなる,っていう話っすね. 言い換えれば, ・(0,0)から 0[rad] 方向に -0.5 だけ進んだ位置 ・(0,0)から 1[rad] 方向に -0.5 だけ進んだ位置 の2か所が白くなる.
chevy

2021/07/09 07:00

補足ありがとうございます。 あと、最初のシェーダー でいうと、 float j = i + 1.0; これをたとえば float j = 0.5 * (i+1.0); のようにしてやれば、もっと小刻みに白い玉を配置できることがわかりました。 (ラジアン を細かくしてあげる=細かい角度ごとに配置できる) また、 destColor += 0.01 / length(q); この0.01を大きくするほど、白い玉は大きくなる =分子が大きければそれだけ値は大きくなる、つまり1になる場合が多くなる ということだとわかりました。 まだまだ自由に形を描画できるようになるには遠そうですが、 がんばって学習していこうと思います。
fana

2021/07/09 07:13

そうね.iのループの回数を増やせばきっと玉も増えるだろうね.
guest

0

フラグメントシェーダー というものが、描画領域を1pxずつ処理する、ということは理解できるのですが、

はい、そうですね。
ですから、mainの中で円を2つ描いている、と思ったら間違いです。

結局gl_FragColorにセットされるのはひとつのvec4なのに、

はい、そうですね。
ですので、そのピクセルが何色になるべきか、を考えればいいのです。

極端に言ってしまえば、円の内側であれば白、円の外側であれば黒、としてしまえば、それだけで円が描けるでしょう。
もっとも、それだときれいな円は描けませんから、円の中心が白、円の中心との距離が離れるに従って黒に近づければ、グラデーションになるでしょう。

そのような考え方を元に、もう一度見直してみてください。

投稿2021/07/08 12:18

katsuko

総合スコア3538

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2021/07/09 02:03

> 白いグラデーションの玉が2つ描画される理由 という問いに対する回答とは何になるのでしょうか? 「各ピクセルが何色になるのかを自分で見直せ」という話で,直接的には問いに答えない形ですか?
katsuko

2021/07/09 12:29

単に質問の意図が「結局gl_FragColorにセットされるのはひとつのvec4なのに、なぜ(省略)玉が描画されるのでしょうか。」というところにあるかと勘違いして、そこに重点を置いて説明しただけです。 別に直接的には答えないなんて考えはありませんよ。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問