GLSL -- OpenGLシェーディング言語 OpenGLのシェーディング言語は、OpenGLのシェーディング・プログラミングや、開発者によって書かれた短いカスタム・プログラムに使用される言語です。これらは、レンダリングパイプラインの固定部分ではなくGPU上で実行され、レンダリングパイプラインのさまざまなレベルをプログラム可能にします。GLSLのシェーダコードは、頂点シェーダとフラグメントシェーダの2つの部分に分かれています。
シェーダーとプロシージャ
- 1.シェーダーの作成とコンパイル
- アプリケーションの作成とリンク
- 3.ユニフォーム変数の取得と設定
- プロパティの取得と設定
- 5.シェーダーコンパイラがバイナリプログラムコードをコンパイル
シェーダーを使ったレンダリングでは、シェーダーオブジェクト+プログラムオブジェクトの2つの基本オブジェクトを作成する必要があります。)バーテックスシェーダオブジェクトとスライスシェーダオブジェクトを作成 -2).各シェーダオブジェクトにソースコードをリンク -3).シェーダオブジェクトをコンパイルします -4).プログラムオブジェクトを作成します -5)。コンパイルしたシェーダオブジェクトをプログラムオブジェクトにリンク -6).プログラムオブジェクトのリンク
- シェーダーの作成とコンパイル
// type - 色合いのタイプを作成する,GL_VERTEX_SHADER またはGL_FRAGMENT_SHADER ,戻り値 - 新しい色合いオブジェクトへのハンドルである。.glDeleteShaderでテクスチャを削除できる。
GLuint glCreateShader(GLenum type); 
// shader - 削除する色合いオブジェクトのハンドル
void glDeleteShader(GLuint shader); 
/*
shader - ツイーターオブジェクトのハンドル
count - インバーターのソース文字列の数は、複数のソース文字列で構成することができるが、各インバーターのメイン関数は1つしかない。
string - カウント数を保持する色合いジェネレータソース文字列の配列へのポインタ。
length - 各タトゥー文字列のサイズを保持する整数の配列へのポインタであり、要素数はcountである。.
*/
void glShaderSource(GLuint shader , GLSizei count ,const GLChar * const *string, const GLint 
*length); 
- アプリケーションの作成とリンク
/*
アプリケーションオブジェクトを作成する
 : 新しいアプリケーション・オブジェクトを実行するためのハンドルを返す。
*/
GLUint glCreateProgram( ) 
/* 
program : 削除が必要なアプリケーションオブジェクトのハンドルを指す。
*/
void glDeleteProgram( GLuint program )
//タトゥーアーティストをアプリケーションに接続する
/*program : アプリケーションオブジェクトへのハンドル
shader : アプリケーションが接続されているtintオブジェクトへのハンドル。
*/
void glAttachShader( GLuint program , GLuint shader ); 
//切断する
/*program : アプリケーションオブジェクトへのハンドル
shader : 切断されたアプリケーションを指す⾊器オブジェクトへのハンドル。
*/
void glDetachShader(GLuint program); 
// program: アプリケーションオブジェクトのハンドルを指す
void glLinkProgram(GLuint program) 
アプリケーションをリンクした後、リンクが成功したかどうかを確認する必要がある。. glGetProgramivでリンク状態を確認できる。: 
/*
program: 情報を取得する必要があるアプリケーションオブジェクトのハンドル
pname : 情報を取得するためのパラメータは: 
GL_ACTIVE_ATTRIBUTES 
GL_ACTIVE_ATTRIBUTES_MAX_LENGTH 
GL_ACTIVE_UNIFORM_BLOCK 
GL_ACTIVE_UNIFORM_BLOCK_MAX_LENGTH 
GL_ACTIVE_UNIFROMS 
GL_ACTIVE_UNIFORM_MAX_LENGTH 
GL_ATTACHED_SHADERS 
GL_DELETE_STATUS 
GL_INFO_LOG_LENGTH 
GL_LINK_STATUS 
GL_PROGRAM_BINARY_RETRIEVABLE_HINT 
GL_TRANSFORM_FEEDBACK_BUFFER_MODE 
GL_TRANSFORM_FEEDBACK_VARYINGS 
GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 
GL_VALIDATE_STATUS 
params : クエリ結果の整数が格納される場所へのポインタ。
*/
void glGetProgramiv (GLuint program,GLenum pname, GLint *params); 
/** アプリケーション情報の日付から情報を取得する
program : 情報を取得する必要があるアプリケーションオブジェクトのハンドルを指す。
maxLength : メッセージの日付を保存するためのキャッシュのサイズ。
length : 日付の情報を書き込む。日付を知る必要がない場合は、このパラメータはNullにすることができる。. 
infoLog : 日付情報が格納されている文字バッファへのポインタ。
void glUseProgram(GLuint program) 
program: アクティブなアプリケーションのアプリケーションオブジェクトにハンドルを設定する.
*/
void glGetPorgramInfoLog( GLuint program ,GLSizei maxLength, GLSizei *length , GLChar *infoLog ) 
カスタムシェーダーによるテクスチャマップの読み込み
GLKitは、Appleがテクスチャイメージを描画するためのカスタムシェーダを不要にした例で、プログラムハンドルプログラムを介して頂点シェーダプログラムの位置に頂点データを渡し、glGetAttributeLocationを使ってシェーダプログラムから頂点データとテクスチャデータを取得します。GLKitはこのようなステップを省き、開発者が使いやすいようにしたAppleの方法ですが、単純な固定シェーダに限定されているため、複雑さが増す場合は、カスタムシェーダを使ってテクスチャイメージを読み込むことができます。
実現のためのステップは以下の通りです。
//
// TextureView.m
// GLSL_DIY_ShaderWithTexture
//
// Created by TL on .
// Copyright 2020 Brain. All rights reserved.
//
#import "TextureView.h"
#import <OpenGLES/ES2/gl.h>
@interface TextureView()
/**
 CALayer  
 */
@property (nonatomic,strong) CAEAGLLayer * mEaglLayer;
@property (nonatomic,strong) EAGLContext * context;
/**
 レンダーバッファ
 */
@property (nonatomic,assign) GLuint mRenderBuffer;
/**
 フレームバッファ
 */
@property (nonatomic,assign) GLuint mFrameBuffer;
/**
 情報へのリンクとして使用されるアプリケーションオブジェクトにハンドラを作成する。
 */
@property (nonatomic,assign) GLuint mPrograme;
@end
@implementation TextureView
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
 // Drawing code
}
*/
-(void)layoutSubviews
{
// 1.描画レイヤーを初期化する。
 [self initSubLayer];
 // 2.コンテキストを設定する
 [self setContext];
 // 3.空のキャッシュ:フレームバッファ& render buffer
 [self deleteRenderBufferAndFrameBuffer];
 
 // 4.renderBufferを設定する
 [self setRenderBuffer];
 
 // 5.frameBufferを設定する
 [self setFrameBuffer];
 
 // 6.レンダリング
 [self renderDisplay];
}
/**
 1.レイヤーを初期化する
 */
- (void)initSubLayer
{
 
 // 1.レイヤーを作成する
 self.mEaglLayer = (CAEAGLLayer *)self.layer;
 
 // 2.スケールを設定する
 [self setContentScaleFactor:[[UIScreen mainScreen]scale]];
 
 /**
 3.説明属性を設定する - レンダリングされたコンテンツを維持しない、カラーフォーマットはRGBA8である。
 kEAGLDrawablePropertyRetainedBacking 表示後に描画面の内容が保持されるかどうかを示す。
 kEAGLDrawablePropertyColorFormat
 このキーの値は、特定のカラーバッファオブジェクトを指定するNSStringである。デフォルトはkEAGLColorFormatRGBA8である;
 
 kEAGLColorFormatRGBA832ビットRGBAカラー,4*8=32 
 kEAGLColorFormatRGB56516ビットRGBカラー,
 kEAGLColorFormatSRGBA8sRGBは標準的な赤、緑、青の略で、CRTモニター、LCDモニター、プロジェクター、プリンター、その他のデバイスの色再現に使用される3つの基本顔料である。sRGBの色空間は別々の色座標に基づいており、これにより、それぞれのデバイスの異なる色座標に依存することなく、異なるデバイスで同じ系統の色に色を伝送することができる。sRGBの色空間は、別々の色座標に基づいており、これによって色は、それらのデバイスの異なる色座標の影響を受けることなく、異なるデバイスとの間で伝送することができる。
 */
 self.mEaglLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking:@NO,
 kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8
 };
 
}
+(Class)layerClass
{
 return [CAEAGLLayer class];
}
/**
 2.コンテキストを設定する
 */
- (void)setContext
{
 // 1.コンテキストを作成する
 self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
 if (!self.context) {
 NSLog(@"init context failed");
 return;
 }
 if (![EAGLContext setCurrentContext:self.context]) {
 NSLog(@"setCurrentContext failed");
 return;
 }
}
/**
 3新しいメソッドを使用する場合は、最初にキャッシュをクリアする必要がある。
 */
- (void)deleteRenderBufferAndFrameBuffer
{
 /*
 buffer フレームバッファとレンダーバッファの2つの主要なカテゴリがある。
 フレームバッファは、レンダーバッファマネージャに相当する。
 frame buffer objectFBOは、カラー、深度、テンプレートバッファ用のアタッチメントポイントオブジェクトのコレクションである。
 render bufferテンプレートには、colorBuffer、depthBuffer、stencilBufferの3種類がある。
 */
 glDeleteBuffers(1,&_mFrameBuffer);
 self.mFrameBuffer = 0;
 
 glDeleteBuffers(1, &_mRenderBuffer);
 self.mRenderBuffer = 0;
 
 
}
/**
 4.レンダーバッファを設定する
 */
- (void)setRenderBuffer
{
 GLuint buffer;
 // 1.キャッシュフラグを要求する
 glGenRenderbuffers(1, &buffer);
 self.mRenderBuffer = buffer;
 // 2.バインドrenderBUffer
 glBindRenderbuffer(GL_RENDERBUFFER, self.mRenderBuffer);
 
 // 3.CAEAGLLayerオブジェクト・ストアをOpenGL ES FreameBufferオブジェクトにバインドする。
 [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.mEaglLayer];
 
}
/**
 5.フレームバッファを設定する
 */
- (void)setFrameBuffer
{
 GLuint buffer;
 // 1.キャッシュフラグを要求する
 glGenBuffers(1, &buffer);
 self.mFrameBuffer = buffer;
 // 2.frameBUfferをバインドする
 glBindFramebuffer(GL_FRAMEBUFFER, self.mFrameBuffer);
 
 /**
 3.glFramebufferRenderbufferを介してGLにrenderBufferをバインドする。_COLOR_ATTACHMENT0 
 フレームバッファを生成したら、レンダーバッファをフレームバッファにバインドする必要がある。,
 対応するアタッチメントポイントにバインドするglFramebufferRenderbuffer関数を呼び出すと、描画が動作する。
 */
 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.mRenderBuffer);
}
/**
 6.レンダリング
 */
- (void)renderDisplay
{
 // 1.クリア画面の色を設定し、カラーバッファをクリアする
 glClear(GL_COLOR_BUFFER_BIT);
 glClearColor(0.6, 0.7, 0.8, 1);
 
 // 2.ビューポートサイズを設定する
 GLfloat scaleF = [[UIScreen mainScreen] scale];
 CGPoint orign = self.frame.origin;
 CGSize size = self.frame.size;
 glViewport(orign.x * scaleF, orign.y * scaleF, size.width * scaleF, size.height * scaleF);;
 
 // 3.s頂点シェーダーを読む+ スライスシェーディングの手順
 NSString * fragmentFile = [[NSBundle mainBundle] pathForResource:@"shader_fragment" ofType:@"fsh"];
 NSString * vertexFile = [[NSBundle mainBundle] pathForResource:@"shader_vertex" ofType:@"vsh"];
 
 NSLog(@"fsh path: %@ 
 vsh path:%@",fragmentFile,vertexFile);
 
 // 4.アプリケーションオブジェクトハンドルに2つのシェーダをロードする。
 self.mPrograme = [self loadShdersWithFragmentFile:fragmentFile WithVextexFile:vertexFile];
 
 // 5.リンクプログラム
 glLinkProgram(self.mPrograme);
 GLint linkStatus;
 // 6.リンクステータスを記録する
 glGetProgramiv(self.mPrograme, GL_LINK_STATUS, &linkStatus);
 if (linkStatus == GL_FALSE) {
 GLchar message;
 glGetProgramInfoLog(self.mPrograme, sizeof(message), 0, &message[0]);
 NSString * messageInfo = [NSString stringWithUTF8String:message];
 NSLog(@"link error messageInfo : %@",messageInfo);
 return;
 }
 NSLog(@"program link success");
 // 7.プログラムを使用する
 glUseProgram(self.mPrograme);
 
 // 8.頂点とテクスチャの座標を設定する
 GLfloat attibuteArr[] = {
 0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
 -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
 -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
 
 0.5f, 0.5f, -1.0f, 1.0f, 1.0f,
 -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
 0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
 };
 
 // 9.頂点データを処理する
 // 1).頂点バッファを開き、バッファ識別子を要求する。
 GLuint attrBuffer;
 glGenBuffers(1, &attrBuffer);
 // 2).attributeBufferをGLにバインドする。_ARRAY_BUFFER 
 glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
 // 3).頂点データをメモリからビデオメモリにコピーし、GPUに仕事をさせる。
 glBufferData(GL_ARRAY_BUFFER, sizeof(attibuteArr), attibuteArr, GL_DYNAMIC_DRAW);
 
 // 10.プログラムを介して頂点シェーダの位置に頂点データを渡す。
 // 1).glGetAttribLocationを呼び出し、頂点属性のデータを取得し、パラメータ2は、シェーダと同じでなければならない。_vertex.vsh テクスチャの位置がビューの位置と同じであれば、テクスチャの位置がビューの位置と同じになることはない。
 GLuint position = glGetAttribLocation(self.mPrograme, "position");
 
 // 2).バッファからデータを読み込む
 glEnableVertexAttribArray(position);
 // 3).読み取り方法を設定する
 /**
 para1: index,頂点データインデックス
 para2: size,頂点属性あたりのコンポーネント数、1、2、3、または4。.デフォルト値は4.
 para3: type,データ内の各コンポーネントのタイプは、一般的に使用されるGLである_FLOAT,GL_BYTE,GL_SHORT初期値はGLである。デフォルトの初期値はGL_FLOAT
 para4: normalized,固定点のデータ値を正規化するか、固定値に変換するか。
 para5: stride,連続する頂点属性間のオフセット、デフォルトは0;
 para6: 配列の最初の頂点プロパティの最初のコンポーネントへのポインタを指定する。デフォルトは0である。
 */
 glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL + 0);
 
 // 11.テクスチャデータを処理する
 // 1).シェーダーから_vertex.fsh テクスチャ座標を取得する
 GLuint textureCoord = glGetAttribLocation(self.mPrograme, "textCoordinate");
 // 2).チャンネルを開き、バッファからデータを読み込む。
 glEnableVertexAttribArray(textureCoord);
 // (float * )NULL,floatに変換しない*,イメージが表示されない
 glVertexAttribPointer(textureCoord, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (float *)NULL + 3);
 
 // イメージテクスチャを読み込む
 [self loadTexture:@"miao.jpg"];
 
 // 12.テクスチャサンプラーsampler2Dを設定する。
 glUniform1i(glGetUniformLocation(self.mPrograme, "colorMap"), 0);
 
 // 13.テクスチャを描く
 glDrawArrays(GL_TRIANGLES, 0, 6);
 
 // 14.レンダーバッファから画面に表示する
 [self.context presentRenderbuffer:GL_RENDERBUFFER];
 
}
/// テクスチャを読み込む
/// @param imageName テクスチャ名
- (GLuint)loadTexture:(NSString *)imageName
{
 // 1.UIImageをCGImageRefに変換する
 CGImageRef spriteImage = [UIImage imageNamed:imageName].CGImage;
 if (!spriteImage) {
 NSLog(@"load image failed");
 return 1;
 }
 
 // 2.イメージサイズ、寸法を取得する
 size_t width = CGImageGetWidth(spriteImage);
 size_t height = CGImageGetHeight(spriteImage);
 // イメージのバイト数を取得し、そのうちRGBAは4オクテットを占める byteData= width * height * 4
 GLubyte * spriteData = (GLubyte *)calloc(width * height * 4, sizeof(GLubyte));
 
 /**
 3.コンテキストを作成する
 para1: data,レンダリングされる描画イメージのメモリアドレス。
 para2: width,bitmapビューの幅をピクセル単位で指定する。
 para3: height,bitmapビューの高さをピクセルで指定する。
 para4: bitPerComponent,ピクセルの各成分のメモリ上のビット数で、例えば32ビットRGBAの場合は8とする。
 para5: bytesPerRow,bitmapイメージの各行に使用されるメモリのビット数
 para6: colorSpace,bitmapkCGImageAlphaPremultipliedLastで使用される色空間:RGBA
 */
 CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
 // 4.CGContextRefでイメージを描画する。
 
 CGRect rect = CGRectMake(0, 0, width, height);
 
 // 5.デフォルトで描画する
 /**
 CGContextDrawImage UIKitフレームワークの原点は画面の左上にあるが、Core Graphicsフレームワークの原点は画面の左下にある。
 CGContextDrawImage
 パラメータ1:描画コンテキスト
 パラメータ2:rect座標
 パラメータ3:描画するイメージ
 */
 CGContextDrawImage(spriteContext, rect, spriteImage);
 // 6.描画が終わったらコンテキストを解放する
 CGContextRelease(spriteContext);
 // 7.テクスチャをデフォルトのテクスチャIDにバインドする。
 glBindTexture(GL_TEXTURE_2D, 0);
 // 8.テクスチャのプロパティを設定する
 /**
 パラメータ1:テクスチャの緯度
 パラメータ2:線形フィルタ、s、t座標のモードを設定する。
 パラメータ3:wrapMode。
 */
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
 float fWidht = width,fHeight = height;
 
 // 9.テクスチャを読み込む
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fWidht, fHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);
 
 // 10.スプライトデータを解放する
 free(spriteData);
 return 0;
 
 
}
/**
 頂点シェーダーをロードする+プログラムプログラムを返すスライスシェーディングプログラム。
 @param fragFile チップシェーダのファイルパス
 @param vertexFile 頂点シェーダファイルパス
 @return program
 */
- (GLuint)loadShdersWithFragmentFile:(NSString *)fragFile WithVextexFile:(NSString *) vertexFile
{
 
 // 1.2つの一時的なシェーダーオブジェクトを定義する
 GLuint verTexShader,fragmentShader;
 // 2.プログラムを作成する
 GLuint program = glCreateProgram();
 // 3.頂点シェーダーをコンパイルする& チップシェーダ
 [self complieShader:&verTexShader type:GL_VERTEX_SHADER file:vertexFile];
 [self complieShader:&fragmentShader type:GL_FRAGMENT_SHADER file:fragFile];
 
 //4. 最終的なアプリケーションを作成する,
 glAttachShader(program, verTexShader);
 glAttachShader(program, fragmentShader);
 
 // 5.不要なシェーダーを解放する
 glDeleteShader(verTexShader);
 glDeleteShader(fragmentShader);
 
 return program;
}
- (void)complieShader:(GLuint *)shader type:(GLenum)type file:(NSString *)fileName
{
 // 1.ファイルパスを取得する
 NSString * contentFile = [NSString stringWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:nil];
 // 2.文字列を変換する
 const GLchar * source = (GLchar*)[contentFile UTF8String];
 // 3.タイプに基づいてシェーダを作成する
 *shader = glCreateShader(type);
 /**
 4. シェーダーのソースコードをシェーダーオブジェクトにアタッチする。
 para1: shader,シェーダーオブジェクトをコンパイルする*shader
 para2: numOfStrings,渡されるソース文字列の数 1
 para3: strings,シェーダのソースコード
 para4: lenOfStrings,length、各文字列の長さを持つ配列、またはNULL、これは文字列がNULL終端であることを意味する。
 */
 glShaderSource(*shader, 1, &source, NULL);
 // 5.シェーダーのソースコードをオブジェクトコードにコンパイルする
 glCompileShader(*shader);
}
@end
ステップに従った後、イメージをロードすることができますが、問題が発生します。
理由は、ビューの開始描画点とテクスチャの開始点が原因として同じではありませんが、テクスチャ上のイメージの描画では、正しい結果を得るために反転させることができます
	// CGContextDrawImage(spriteContext)に追加する。, rect, spriteImage);  
 // xにパンする,y
	CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
 // もう一度イメージの高さをパンする
	CGContextTranslateCTM(spriteContext, 0, rect.size.height);
 // Y軸に沿って反転する
	CGContextScaleCTM(spriteContext, 1.0, -1.0);
 // 次に、元の位置にパンする
	CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
 // 再描画する
 CGContextDrawImage(spriteContext, rect, spriteImage);




