0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ofVbo#drawXxxの実装

Last updated at Posted at 2019-11-22

ofVbo#drawXxxの実装

シェーダ周りで気になることがいくつかあったので調べてみました。
基準となるパスは openframeworks/libs/openframeworks です。

  • どういう場合にデフォルトシェーダが設定されるかを確認します。
  • シェーダにデフォルトで設定される変数を確認します。

バージョン

of_v0.10.1_vs2017_release

調査内容

依存関係の浅いところから並べています。

./gl/ofVbo.cpp

drawXxx()
ofGetGLRenderer() を読んでいく


//--------------------------------------------------------------
void ofVbo::draw(int drawMode, int first, int total) const{
	ofGetGLRenderer()->draw(*this,drawMode,first,total);
}

//--------------------------------------------------------------
void ofVbo::drawElements(int drawMode, int amt, int offsetelements) const{
	ofGetGLRenderer()->drawElements(*this,drawMode,amt,offsetelements);
}

//--------------------------------------------------------------
void ofVbo::drawInstanced(int drawMode, int first, int total, int primCount) const{
	ofGetGLRenderer()->drawInstanced(*this,drawMode,first,total,primCount);
}

//--------------------------------------------------------------
void ofVbo::drawElementsInstanced(int drawMode, int amt, int primCount) const{
	ofGetGLRenderer()->drawElementsInstanced(*this,drawMode,amt,primCount);
}

./gl/ofGLUtils.cpp

ofGetGLRenderer()
環境によってレンダラを切り替えられる模様
プログラマブルシェーダでないときに上の実装が使われる

# ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
# else
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLProgrammableRenderer>();
	}
}
# endif

./utils/ofConstants.h

筆者はWindows10を使っているので TARGET_WIN32 だけが定義される。
なので、先程のメソッドは上のものが使用される。

// 		helpful:
// 		http://www.ogre3d.org/docs/api/html/OgrePlatform_8h-source.html

# if defined( __WIN32__ ) || defined( _WIN32 )
	#define TARGET_WIN32
# elif defined( __APPLE_CC__)
    #define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
    #include <TargetConditionals.h>
	#if (TARGET_OS_IPHONE || TARGET_OS_IOS || TARGET_OS_SIMULATOR || TARGET_OS_IPHONE_SIMULATOR) && !TARGET_OS_TV && !TARGET_OS_WATCH
        #define TARGET_OF_IPHONE
        #define TARGET_OF_IOS
        #define TARGET_OPENGLES
        #include <unistd.h>
    #elif TARGET_OS_TV
        #define TARGET_OF_IOS
        #define TARGET_OF_TVOS
        #define TARGET_OPENGLES
        #include <unistd.h>
    #elif TARGET_OS_WATCH
        #define TARGET_OF_IOS
        #define TARGET_OF_WATCHOS
        #define TARGET_OPENGLES
        #include <unistd.h>
	#else
		#define TARGET_OSX
	#endif
# elif defined (__ANDROID__)
	#define TARGET_ANDROID
	#define TARGET_OPENGLES
# elif defined(__ARMEL__)
	#define TARGET_LINUX
	#define TARGET_OPENGLES
	#define TARGET_LINUX_ARM
# elif defined(__EMSCRIPTEN__)
	#define TARGET_EMSCRIPTEN
	#define TARGET_OPENGLES
	#define TARGET_NO_THREADS
	#define TARGET_PROGRAMMABLE_GL
	#define TARGET_IMPLEMENTS_URL_LOADER
# else
	#define TARGET_LINUX
# endif

./gl/ofGLUtils.cpp(再掲)

ofGetGLRenderer()
ofGetCurrentRenderer() を読んでいく

# ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
# else
...

中略

...
# endif

./app/ofAppRunner.cpp

ofGetCurrentRenderer()
getCurrentWindow() を読んでいく

//--------------------------------------
shared_ptr<ofBaseRenderer> & ofGetCurrentRenderer(){
	return mainLoop()->getCurrentWindow()->renderer();
}

./app/ofAppRunner.cpp

mainLoop()
メインループを生成して返す。
staticとついているので一度しか生成されない。

//--------------------------------------
namespace{
    shared_ptr<ofMainLoop> & mainLoop(){
        static shared_ptr<ofMainLoop> * mainLoop(new shared_ptr<ofMainLoop>(new ofMainLoop));
        return *mainLoop;
    }

中略
}

./app/ofMainLoop.cpp

getCurrentWindow()
ofMainLoopのコンストラクタではcurrentWindowを設定していないので、setしているところを探す。

shared_ptr<ofAppBaseWindow> ofMainLoop::getCurrentWindow(){
	return currentWindow.lock();
}

./app/ofMainLoop.cpp

setCurrentWindow()


void ofMainLoop::setCurrentWindow(shared_ptr<ofAppBaseWindow> window){
	currentWindow = window;
}

void ofMainLoop::setCurrentWindow(ofAppBaseWindow * window){
	if(currentWindow.lock().get() == window){
		return;
	}
	for(auto i: windowsApps){
		if(i.first.get() == window){
			currentWindow = i.first;
			break;
		}
	}
}

./app/ofAppGLFWWindow.cpp

setCurrent()
setCurrentWindow() を読んでいるのはここだけ。
実はofAppBaseGLWindowのサブクラスにはofAppGlutWindowというのもあるのですが、
こちらでは setCurrentWindow() を読んでいないみたいです。
こちらのリンク曰く、GLUTからGLFWに移行したとのことなのでこれであっているのでしょうか
であるなら、先程のmainLoop()->getCurrentWindow()->renderer()ofAppGLFWWindowの renderer() が呼ばれているはず。

//------------------------------------------------------------
ofAppGLFWWindow * ofAppGLFWWindow::setCurrent(GLFWwindow* windowP){
	ofAppGLFWWindow * instance = static_cast<ofAppGLFWWindow *>(glfwGetWindowUserPointer(windowP));
	shared_ptr<ofMainLoop> mainLoop = ofGetMainLoop();
	if(mainLoop){
		mainLoop->setCurrentWindow(instance);
	}
	instance->makeCurrent();
	return instance;
}

./app/ofAppGLFWWindow

renderer()
currentRendererは setup() で初期化されます。

//--------------------------------------------
shared_ptr<ofBaseRenderer> & ofAppGLFWWindow::renderer(){
	return currentRenderer;
}

setup()
実際にはかなり長いメソッドですが、currentRendererを初期化してる部分に絞って抜粋。
GLESか否かで分岐しますが、どちらもofGLProgrammableRendererofGLRendererを選択するようです。
TARGET_OPENGLES./utils/ofConstants.h でも登場していますが、モバイル端末などで選択されるフラグのようです。
また、筆者の環境ではOpenGLのメジャーバージョンは4なのでofGLProgrammableRendererへ進みます。


void ofAppGLFWWindow::setup(const ofGLFWWindowSettings & _settings){
	...
	中略
	...
	glfwWindowHint(GLFW_SAMPLES, settings.numSamples);
	glfwWindowHint(GLFW_RESIZABLE, settings.resizable);
	glfwWindowHint(GLFW_DECORATED, settings.decorated);
    #ifdef TARGET_OPENGLES
	    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, settings.glesVersion);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
		glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
		if(settings.glesVersion>=2){
			currentRenderer = std::make_shared<ofGLProgrammableRenderer>(this);
		}else{
			currentRenderer = std::make_shared<ofGLRenderer>(this);
		}
    #else
	    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, settings.glVersionMajor);
		glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, settings.glVersionMinor);
		if((settings.glVersionMajor==3 && settings.glVersionMinor>=2) || settings.glVersionMajor>=4){
			glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
		}
		if(settings.glVersionMajor>=3){
			glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
			currentRenderer = std::make_shared<ofGLProgrammableRenderer>(this);
		}else{
			currentRenderer = std::make_shared<ofGLRenderer>(this);
		}
    #endif
		...
		中略
		...
# endif
}

./gl/ofGLUtils.cpp(再掲)

ofGetGLRenderer()
ofGetCurrentRenderer() が ofGLProgrammableRenderer を返すことがわかったので、
getType() の実装へ進みます。

# ifndef TARGET_PROGRAMMABLE_GL
shared_ptr<ofBaseGLRenderer> ofGetGLRenderer(){
	if(ofGetCurrentRenderer()->getType()==ofGLRenderer::TYPE || ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPE){
		return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();
	}else if(ofGetCurrentRenderer()->getType()==ofRendererCollection::TYPE){
		return ((shared_ptr<ofRendererCollection>&)ofGetCurrentRenderer())->getGLRenderer();
	}else{
		return shared_ptr<ofGLRenderer>();
	}
}
# else
...

中略

...
# endif

./gl/ofGLProgrammableRenderer.h

文字列を返すようです。

...
中略
...
    static const std::string TYPE;
	const std::string & getType(){ return TYPE; }

...
中略
... 

./gl/ofGLProgrammableRenderer.cpp

ということで、
ofGetCurrentRenderer()->getType()==ofGLProgrammableRenderer::TYPEは true を返します。

const string ofGLProgrammableRenderer::TYPE="ProgrammableGL";

./gl/ofGLUtils.cpp(再掲)

よって、呼び出されるのはこれです。
戻り値は ofGLProgrammableRenderer

return (shared_ptr<ofBaseGLRenderer>&)ofGetCurrentRenderer();

./gl/ofGLProgrammableRenderer.cpp

draw()
なので、ofVbo#draw はこれを呼んでいる。

//----------------------------------------------------------
void ofGLProgrammableRenderer::draw(const ofVbo & vbo, GLuint drawMode, int first, int total) const{
	if(vbo.getUsingVerts()) {
		vbo.bind();
		const_cast<ofGLProgrammableRenderer*>(this)->setAttributes(vbo.getUsingVerts(),vbo.getUsingColors(),vbo.getUsingTexCoords(),vbo.getUsingNormals());
		glDrawArrays(drawMode, first, total);
		vbo.unbind();
	}
}

setAttributes()

//----------------------------------------------------------
void ofGLProgrammableRenderer::setAttributes(bool vertices, bool color, bool tex, bool normals){
	bool wasColorsEnabled = colorsEnabled;
	bool wasUsingTexture = texCoordsEnabled & (currentTextureTarget!=OF_NO_TEXTURE);

	texCoordsEnabled = tex;
	colorsEnabled = color;
	normalsEnabled = normals;

	if(!uniqueShader || currentMaterial){
		beginDefaultShader();
	}

	bool usingTexture = tex & (currentTextureTarget!=OF_NO_TEXTURE);
	if(wasUsingTexture!=usingTexture){
		if(currentShader) currentShader->setUniform1f(USE_TEXTURE_UNIFORM,usingTexture);
	}
	if(wasColorsEnabled!=color){
		if(currentShader) currentShader->setUniform1f(USE_COLORS_UNIFORM,color);
	}
}

beginDefaultShader()
ここでデフォルトで適用するシェーダを決める。
oFのデフォルトの Xxx#draw ではこのシェーダが使われていると思われる。
ofGetCurrentRenderer()->draw()で検索すると、具体的にどのクラスで使われているかわかる。
ofGetCurrentRenderer() == ofGetGLRenderer()であることはすでに分かっているため

//----------------------------------------------------------
void ofGLProgrammableRenderer::beginDefaultShader(){
	if(usingCustomShader && !currentMaterial)	return;

	const ofShader * nextShader = nullptr;

	if(!uniqueShader || currentMaterial){
        if(currentMaterial){
            nextShader = &currentMaterial->getShader(currentTextureTarget,colorsEnabled,*this);

		}else if(bitmapStringEnabled){
			nextShader = &bitmapStringShader;

		}else if(colorsEnabled && texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexColor;
				break;
	#endif
			}

		}else if(colorsEnabled){
			nextShader = &defaultNoTexColor;

		}else if(texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectNoColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DNoColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexNoColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexNoColor;
				break;
	#endif
			}

		}else{
			nextShader = &defaultNoTexNoColor;
		}

	}else{
		nextShader = &defaultUniqueShader;
	}

	if(nextShader){
        if(!currentShader || *currentShader!=*nextShader){
			settingDefaultShader = true;
			bind(*nextShader);
			settingDefaultShader = false;
		}
	}
}

setAttributes()(再掲)
以下の条件を満たさなければカスタムしたシェーダを使えることがわかる。

//----------------------------------------------------------
void ofGLProgrammableRenderer::setAttributes(bool vertices, bool color, bool tex, bool normals){
	...
	中略
	...

	if(!uniqueShader || currentMaterial){
		beginDefaultShader();
	}
	...
	中略
	...
}

./app/ofGLProgrammableRenderer.cpp

コンストラクタではfalseに設定されており、uniqueShader が trueになるのはここだけ。
ということは、ラズパイ環境でないかぎり必ず beginDefaultShader() は呼ばれてしまう。

# ifdef TARGET_RASPBERRY_PI
	uniqueShader = true;
# else
	uniqueShader = false;
# endif

beginDefaultShader()(再掲)

  • カスタムシェーダが使われている || マテリアルがある ときになにもしないことがわかる。
  • 次のシェーダがある && 現在のシェーダがある && 現在のシェーダと次のシェーダが違うときにデフォルトシェーダがバインドされることがわかる。
//----------------------------------------------------------
void ofGLProgrammableRenderer::beginDefaultShader(){
	if(usingCustomShader && !currentMaterial)	return;

	const ofShader * nextShader = nullptr;

	if(!uniqueShader || currentMaterial){
        if(currentMaterial){
            nextShader = &currentMaterial->getShader(currentTextureTarget,colorsEnabled,*this);

		}else if(bitmapStringEnabled){
			nextShader = &bitmapStringShader;

		}else if(colorsEnabled && texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexColor;
				break;
	#endif
			}

		}else if(colorsEnabled){
			nextShader = &defaultNoTexColor;

		}else if(texCoordsEnabled){
			switch(currentTextureTarget){
	#ifndef TARGET_OPENGLES
			case GL_TEXTURE_RECTANGLE_ARB:
				nextShader = &defaultTexRectNoColor;
				break;
	#endif
			case GL_TEXTURE_2D:
				nextShader = &defaultTex2DNoColor;
				break;
			case OF_NO_TEXTURE:
				nextShader = &defaultNoTexNoColor;
				break;
	#ifdef TARGET_ANDROID
			case GL_TEXTURE_EXTERNAL_OES:
				nextShader = &defaultOESTexNoColor;
				break;
	#endif
			}

		}else{
			nextShader = &defaultNoTexNoColor;
		}

	}else{
		nextShader = &defaultUniqueShader;
	}

	if(nextShader){
        if(!currentShader || *currentShader!=*nextShader){
			settingDefaultShader = true;
			bind(*nextShader);
			settingDefaultShader = false;
		}
	}
}

bind()
usingCustomShader を true にしているのはここだけ。

//----------------------------------------------------------
void ofGLProgrammableRenderer::bind(const ofShader & shader){
    if(currentShader && *currentShader==shader){
		return;
    }
	glUseProgram(shader.getProgram());

	currentShader = &shader;
	uploadMatrices();
	setDefaultUniforms();
	if(!settingDefaultShader){
		usingCustomShader = true;
	}
}

./gl/ofShader.cpp

begin()
そして、ofShader#beginではこれを呼んでいるので
ちゃんとそのように書いていればシェーダは切り替わるはず。
ただし、glUseProgram(myShader); のあとに ofObject#draw とした場合には上書きされてしまう可能性がある。

//--------------------------------------------------------------
void ofShader::begin()  const{
	ofGetGLRenderer()->bind(*this);
}

./app/ofGLProgrammableRenderer.cpp

uploadMatrices(), setDefaultUniforms()
これは ofShader::bind から呼ばれる。
デフォルトで定義されるシェーダ変数にどんな値が入っているかがわかる。

//----------------------------------------------------------
void ofGLProgrammableRenderer::uploadMatrices(){
	if(!currentShader) return;
	currentShader->setUniformMatrix4f(MODEL_MATRIX_UNIFORM, matrixStack.getModelMatrix());
	currentShader->setUniformMatrix4f(VIEW_MATRIX_UNIFORM, matrixStack.getViewMatrix());
	currentShader->setUniformMatrix4f(MODELVIEW_MATRIX_UNIFORM, matrixStack.getModelViewMatrix());
	currentShader->setUniformMatrix4f(PROJECTION_MATRIX_UNIFORM, matrixStack.getProjectionMatrix());
	currentShader->setUniformMatrix4f(TEXTURE_MATRIX_UNIFORM, matrixStack.getTextureMatrix());
	currentShader->setUniformMatrix4f(MODELVIEW_PROJECTION_MATRIX_UNIFORM, matrixStack.getModelViewProjectionMatrix());
	if(currentMaterial){
		currentMaterial->uploadMatrices(*currentShader,*this);
	}
}

//----------------------------------------------------------
void ofGLProgrammableRenderer::setDefaultUniforms(){
	if(!currentShader) return;
	currentShader->setUniform4f(COLOR_UNIFORM, currentStyle.color.r/255.,currentStyle.color.g/255.,currentStyle.color.b/255.,currentStyle.color.a/255.);
	bool usingTexture = texCoordsEnabled & (currentTextureTarget!=OF_NO_TEXTURE);
	currentShader->setUniform1f(USE_TEXTURE_UNIFORM,usingTexture);
	currentShader->setUniform1f(USE_COLORS_UNIFORM,colorsEnabled);
	if(currentMaterial){
		currentMaterial->updateMaterial(*currentShader,*this);
		currentMaterial->updateLights(*currentShader,*this);
	}
}

./gl/ofShader.cpp

setUniformMatrix4f()

デフォルトの変数が見つからなかった場合にはちゃんと何もしないようになっている。
(他のsetUniformXxxも同様)

//--------------------------------------------------------------
void ofShader::setUniformMatrix4f(const string & name, const glm::mat4 & m, int count) const{
	if(bLoaded) {
		int loc = getUniformLocation(name);
		if (loc != -1) glUniformMatrix4fv(loc, count, GL_FALSE, glm::value_ptr(m));
	}
}

ライセンス

ここで転載したソースコードはMITライセンスで公開されています。
全文

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?