30
23

More than 5 years have passed since last update.

GLSLの冒頭の1行目について丁寧に解説する

Last updated at Posted at 2019-05-20

はじめに

冒頭の1行と言ってますが,正確には違うのでマジレスはご容赦:pray:
GLSLを書いているといるよく見かける冒頭の1行がありますよね,これです↓

_.frag
vec2 uv = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);

GLSL初学者の人にとっては結構1行に情報が詰まっていて先頭行から難しいんじゃないかなと思ったので(少なくとも自分が勉強し始めた時はよくわからなかった)これについて説明していこうと思います.

そもそもの目的

そもそも上記のコードを使う目的なんですが,画面の縦横比を1:1に揃えるために使用します.
そうしたくなる理由を含め,順を追ってみていきましょう.

デフォルト状態

sample.frag
precision mediump float;
uniform float time;
uniform vec2 resolution;

void main() {
    vec2 uv = (gl_FragCoord.xy) / resolution.xy;
    gl_FragColor = vec4(uv.x, 0.0, uv.y , 1.0);
}

bandicam 2019-05-20 18-43-18-190.jpg

それぞれの組み込み変数について

  • gl_FragCoord.xy : ピクセル座標.要するに何番目のピクセルにいるかっていうvec2型変数です.
    そして左下が(0.0, 0.0)で右上へ向かって大きくなっていきます.

  • resolution : 解像度

なので,以下のコードでvec2(0.0, 0.0) ~ vec2(1.0, 1.0)までに正規化を行っているですね.

_.frag
vec2 uv = (gl_FragCoord.xy) / resolution.xy;

正規化されているか検証

出力カラーを原点(左下)からの距離のマップにしてみます.

sample.frag
precision mediump float;
uniform float time;
uniform vec2 resolution;

void main() {
    vec2 uv = (gl_FragCoord.xy) / resolution.xy;
    float gray = length(uv);
    gl_FragColor = vec4(vec3(gray) , 1.0);
}

bandicam 2019-05-20 18-49-36-902.jpg

正規化後の座標系は左下が(0.0, 0.0), 右上が(1.0, 1.0)なので原点(左下)からの距離が遠くになるにつれて白くなっているのでうまくいってそう:clap::clap:

問題点

ここで1つ絵を作っていると問題点が上がってきます.それは縦横比です.上の画像のように横に引き伸ばされちゃってます..これをどうにか解決したい..そこで出てくるのが冒頭のプログラムです

本題

周りくどいようですが,この記事ではできるだけ丁寧に追っていきたいのでステップに分けて説明していきます.

ステップ1(0.0~1.0 -> -1.0 ~ 1.0へ)

今までの座標系ってvec2(0.0, 0.0)からvec2(1.0, 1.0)だったわけですが,これからはvec2(-1.0, -1.0)からvec2(1.0, 1.0)へ変換します.

sample.frag
precision mediump float;
uniform float time;
uniform vec2 resolution;

void main() {
    vec2 uv = (gl_FragCoord.xy*2.0 - resolution) / resolution.xy;
  float gray = length(uv);
    gl_FragColor = vec4(vec3(gray) , 1.0);
}

gl_FragCoord.xy*2.0 - resolution / resolution.xy : これで-1.0 ~ 1.0になります.
例えばresolution.xが1280だとします.gl_FragCoord.xが0.0の時上の式の結果は-1.0
gl_FragCoord.xが1280.0のとき1.0ですよね.
GLSL難しいんですけど結構具体的に数値入れて考えてみると理解出来ます.

bandicam 2019-05-20 19-09-39-257.jpg

これで原点(中心になった)vec2(0.0, 0.0)からの距離のマップを確認できました.
次で最終的な縦横比を解決していきます.

ステップ2(いよいよ本題)

いよいよ比率を合わせていく最終ステップです.
要するに今解決したいことってuvで考えるとx方向に1.0進む長さとy方向に1.0進む長さが違うから困っているんですよね.そしたらそれを合わせてあげるだけです.1歩引いて考えてみると超簡単なので安心してください:pray:

sample.frag
precision mediump float;
uniform float time;
uniform vec2 resolution;

void main() {
    vec2 uv = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
  float gray = length(uv);
    gl_FragColor = vec4(vec3(gray) , 1.0);
}

min : 小さい方を返す
今まではresolution.xyそれぞれの要素で割っていたんですけど,minを使いresolutionの小さい方を選択し割る数を統一してあげればvec2(0.0, 0.0)から同じ分だけのピクセル分を進めば同じ分だけuvが変化するってことです.
具体例で考えてみましょう.resolutionを(1280, 720)として中心からy軸に関して正方向に720ピクセル進んだpixel1と,x軸に関して正に720ピクセル進んだpixel2についてそれぞれuvを考えます.
今までのようにそれぞれのresolutionのxyで割るとpixel1のuvは(0.0, 1.0), pixel2は(720/1280, 0.0)となってしまいます.ですが小さい方のresolutionの要素(今回は720)で割ってあげればpixel1のuvは(0.0, 720/720), pixel2は(720/720, 0.0)これでどっちも原点から等しい距離になりましたね.
これで縦横1:1になりました.
注意しなければならないなのは小さい方のresolutionの値で割る部分です.大きい方で割ってしまうと1方の軸のマックスの値は1.0を下回ってしまいますよね.なので小さい方に合わせてあげます.

bandicam 2019-05-20 19-53-33-965.jpg

いまいちしっくりこない方

多分,下記のコード見たらもう少しすっきりするかなと思います.min(resolution.xy)では上の場合resolution.yが返り値として得られます.なのでx方向に関しては以前よりも小さいな値で割ることになるのでuv.xは1.0を超えます.なので以下のコードではuv.xが-1.0 もしくは1.0を超えた場合塗る色を黒にしています.これで縦横1:1をより意識できるのではないでしょうか?

sample.frag
precision mediump float;
uniform float time;
uniform vec2 resolution;


void main() {
   vec2 uv = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
     vec4 col = vec4(1.0);

    if(abs(uv.x) > 1.0){
        col = vec4(0.0, 0.0, 0.0, 1.0);
    }
    else{
        col = vec4(uv.x, 0.0,uv.y, 1.0);
    }
   gl_FragColor = vec4(col);
}

bandicam 2019-05-20 19-20-40-432.jpg

30
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
23