2014-12-07 3 views
1

У меня есть приложение kivy, где я хочу, чтобы все, что нарисовано на определенном RenderContext, использовало эффект пост-обработки шейдера (подобно эффектам, продемонстрированным в примере EffectWidget в примерах kivy в документации).Как изменить фрагментарный шейдер киви RenderContext?

В конструкторе world Widget я создать RenderContext

self.prc = RenderContext() 

установить свою матрицу проекции (это работает)

self.prc['projection_mat'] = proj_mat 

, а затем попытаться установить его фрагмент шейдеры до минимальной копии по умолчанию фрагментарный шейдер, который должен просто сделать все на одну десятую непрозрачным (в основном затемняя экран).

self.prc.shader.fs = """ 
#ifdef GL_ES 
    precision highp float; 
#endif 

/* Outputs from the vertex shader */ 
varying vec4 frag_color; 
varying vec2 tex_coord0; 

/* uniform texture samplers */ 
uniform sampler2D texture0; 

void main (void){ 
    gl_FragColor = 0.1*frag_color * texture2D(texture0, tex_coord0); 
} 
""" 

Если код шейдера неверен, программа не запускается, жалуясь шейдера ошибка компиляции, которая указывает, что шейдер компилируется. Но я не вижу никакого эффекта шейдера. Все сделанное на prc нарисовано, но при нормальной непрозрачности. Что я делаю не так? Спасибо за ваше время!


EDIT!

Мне было предложено предоставить полный рабочий пример. Следующая программа рисует два прямоугольника. Прямоугольник слева имеет свой собственный RenderContext и не подвержен влиянию постобработки в оттенках серого (он нарисован как красный). Прямоугольник справа не имеет собственного RenderContext, а -, на который влияет влияние постобработки.

Вот код:

from kivy.app import App 
from kivy.uix.widget import Widget 
from kivy.uix.floatlayout import FloatLayout 
from kivy.graphics import * 
from kivy.graphics.opengl import * 
from kivy.graphics.shader import * 
from kivy.core.window import Window 
from kivy.graphics.transformation import Matrix 

from kivy.logger import Logger 

class World(Widget) : 
    def __init__(self, **kwargs): 
     Logger.debug('world.init()') 

     # Parent RenderContext for subsuming all other render contexts 
     self.prc=RenderContext() 
     proj_mat = Matrix() 
     proj_mat.look_at(0.,0.,1., # eye position coords 
         0.,0.,0., # looking at these coords 
         0,1.,0) # a vector that points up 

     if Window.height > Window.width : 
      self.xRadius = float(Window.width)/Window.height 
      self.yRadius = 1.0 
      proj_mat.scale(1.0/self.xRadius,1.0,1.0) 
     else : 
      self.xRadius = 1.0 
      self.yRadius = float(Window.height)/Window.width 
      proj_mat.scale(1.0,1.0/self.yRadius,1.0) 

     self.prc['projection_mat'] = proj_mat 

     ## an effect shader used to make objects monochromatic (grayscale) 
     self.prc.shader.fs = """ 
#ifdef GL_ES 
precision highp float; 
#endif 

/* Outputs from the vertex shader */ 
varying vec4 frag_color; 
varying vec2 vTexCoords0; 

/* uniform texture samplers */ 
uniform sampler2D texture0; 

uniform vec2 resolution; 
uniform float time; 

void main() { 
    vec4 rgb = texture2D(texture0, vTexCoords0); 
    float c = (rgb.x + rgb.y + rgb.z) * 0.3333; 
    gl_FragColor = vec4(c, c, c, 1.0); 
} 
""" 

     if not self.prc.shader.success : 
      raise Exception('Effect shader compile failed.') 

     self.canvas = self.prc 

     ## Left Rectangle drawn with its own RenderContext 
     ## this is not affected by the effect shader (if it were, it would be drawn as white) 
     ## but it is affected by the parent's projection matrix 
     self.spriteRC = RenderContext(use_parent_projection=True) 
     self.spriteRC.add(Color(1,0,0,1)) 
     self.spriteRC.add(Rectangle(pos=(-0.25,0.0),size=(0.1,0.1))) 

     ## Right Rectangle object drawn directly to the canvas 
     ## this **is** affected by the effect shader 
     self.canvas.add(Color(1,0,0,1)) 
     self.canvas.add(Rectangle(pos=(0.25,0),size=(0.1,0.1))) 
     self.canvas.add(self.spriteRC) 



     super(World, self).__init__(**kwargs) 

class GameApp(App): 
    def build(self): 
     w = World() 
     fl = FloatLayout() 
     fl.add_widget(w) 
     return fl 

if __name__ == '__main__': 
    GameApp().run() 
+0

Я думаю, что мой подход может быть неправильным, и, возможно, функциональность, которую я хочу, лучше обеспечивается классом 'fbo' kivy. Не уверен, хотя, исследуя это сейчас. – weemattisnot

+0

Чтобы запустить образец кода, мне пришлось заменить '' vTexCoords0'' на '' tex_coord0'' в шейдере. У меня работает kivy 1.9.1. – Rolf

ответ

5

Shader не могут быть уложены в трубопроводе. Будет использоваться только последнее связанное, это не ограничение Kivy, но так работает OpenGL. Т.е.:

self.c1 = RenderContext() 
self.c2 = RenderContext() 
self.c2.add(Rectangle()) 
self.c1.add(self.c2) 

Прямоугольник будет обработан только последним шейдером, то есть в c2.

Чтобы создать определенный шейдер для прямоугольника, а затем обработать выход с помощью шейдера c1, используйте Framebuffer (Fbo - подкласс RenderContext)!

self.c1 = RenderContext() 
self.c2 = Fbo(size=...) 
self.c2.add(Rectangle()) 
self.c1.add(self.c2) # this is just for triggering the render from c1 when c2 content changes 
self.c1.add(Rectangle(size=self.c2.size, texture=self.c2.texture)) 

Я пропустил весь цвет и другие параметры здесь, но это просто для демонстрации. Вы можете изменить шейдер на Fbo так же, как и на RenderContext.

Смежные вопросы