Skip to content

Commit

Permalink
Add Shader class
Browse files Browse the repository at this point in the history
  • Loading branch information
Trufi committed Jul 18, 2016
1 parent 6f57bb8 commit 2a6b0a7
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 68 deletions.
75 changes: 75 additions & 0 deletions src/Shader.js
@@ -0,0 +1,75 @@
/**
* Шейдер компилирует код и хранит его в видеокарте.
* Один шейдер может быть использован для нескольких программ.
*
* @param {String} type Тип шейдера: или vertex, или fragment
* @param {String | String[]} code Код шейдера написанный на языке GLSL.
* Можно передать несколько строк в виде массива, тогда перед компиляцией строки сложатся.
* @param {Object[]} [definitions=[]]
*/
class Shader {
constructor(type, code, definitions = []) {
/**
* Тип шейдера
* @type {Shader.Vertex | Shader.Fragment}
*/
this.type = type === 'vertex' ? Shader.Vertex : Shader.Fragment;

/**
* Код шейдера
* @type {String}
* @ignore
*/
this._code = Array.isArray(code) ? code.join('\n') : (code || '');

this._code = definitions.map(def => {
if (def.value !== undefined) {
return '#define ' + def.type + ' ' + def.value;
} else {
return '#define ' + def.type;
}
}).join('\n') + '\n' + this._code;
}

/**
* Возвращает webgl шейдер для связывания с программой.
* Если шейдер используюется первый раз, то компилирует его.
*/
get(gl) {
if (!this._shader) {
this._compile(gl);
}
return this._shader;
}

/**
* Удаляет шейдер из видеокарты
* @param {WebGLRenderingContext} gl Контекст WebGl
*/
remove(gl) {
if (this._shader) {
gl.deleteShader(this._shader);
}
}

/**
* Компилирует данный шейдер
* @param {WebGLRenderingContext} gl Контекст WebGL
* @ignore
*/
_compile(gl) {
const glType = this.type === Shader.Vertex ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER;
const shader = this._shader = gl.createShader(glType);
gl.shaderSource(shader, this._code);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
}
}
}

Shader.Vertex = 1;
Shader.Fragment = 2;

export default Shader;
56 changes: 9 additions & 47 deletions src/ShaderProgram.js
Expand Up @@ -5,18 +5,17 @@ import ShaderUniform from './ShaderUniform';
* Шейдерная программа инициализирует шейдеры, подготавливает и связывает данные с WebGL.
*
* @param {Object} options
* @param {String} vertex Код вершинного шейдера
* @param {String} fragment Код фрагментного шейдера
* @param {Shader} vertex Вершинный шейдер
* @param {Shader} fragment Фрагментный шейдер
* @param {UniformDefinition[]} [options.uniforms=[]] Описание юниформ
* @param {AttributeDefinition[]} [options.attributes=[]] Описание атрибутов
* @param {Object[]} [options.definitions=[]]
*/
class ShaderProgram {
constructor(options) {
options = options || {};

this._vertexShaderCode = options.vertex || '';
this._fragmentShaderCode = options.fragment || '';
this._vertexShader = options.vertex;
this._fragmentShader = options.fragment;

this._uniforms = {};
options.uniforms = options.uniforms || [];
Expand All @@ -30,8 +29,6 @@ class ShaderProgram {
this._attributes[obj.name] = new ShaderAttribute(obj);
});

this._definitions = options.definitions || [];

this._status = ShaderProgram.NOT_READY;
}

Expand Down Expand Up @@ -93,35 +90,16 @@ class ShaderProgram {
}

_prepare(gl) {
this._prepareShaders(gl);
this._prepareAttributes(gl);
this._prepareUniforms(gl);
}

_prepareShaders(gl) {
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, this._addDefinitions(this._fragmentShaderCode));
gl.compileShader(fragmentShader);
this._webglProgram = gl.createProgram();

if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(fragmentShader));
this._status = ShaderProgram.FAILED;
return;
if (this._vertexShader) {
gl.attachShader(this._webglProgram, this._vertexShader.get(gl));
}

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, this._addDefinitions(this._vertexShaderCode));
gl.compileShader(vertexShader);

if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(vertexShader));
this._status = ShaderProgram.FAILED;
return;
if (this._fragmentShader) {
gl.attachShader(this._webglProgram, this._fragmentShader.get(gl));
}

this._webglProgram = gl.createProgram();
gl.attachShader(this._webglProgram, vertexShader);
gl.attachShader(this._webglProgram, fragmentShader);
gl.linkProgram(this._webglProgram);

if (!gl.getProgramParameter(this._webglProgram, gl.LINK_STATUS)) {
Expand All @@ -131,27 +109,11 @@ class ShaderProgram {
}

this._status = ShaderProgram.READY;
this._fragmentShaderCode = null;
this._vertexShaderCode = null;
}

_addDefinitions(shader) {
return this._definitions.map(def => {
if (def.value !== undefined) {
return '#define ' + def.type + ' ' + def.value;
} else {
return '#define ' + def.type;
}
}).join('\n') + '\n' + shader;
}

_prepareAttributes(gl) {
for (const name in this._attributes) {
this._attributes[name].setLocation(gl, this._webglProgram);
}
}

_prepareUniforms(gl) {
for (const name in this._uniforms) {
this._uniforms[name].setLocation(gl, this._webglProgram);
}
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Expand Up @@ -9,6 +9,7 @@ import PerspectiveCamera from './cameras/PerspectiveCamera';
import OrthographicCamera from './cameras/OrthographicCamera';
import Buffer from './Buffer';
import Geometry from './Geometry';
import Shader from './Shader';
import ShaderProgram from './ShaderProgram';
import BasicMeshMaterial from './materials/BasicMeshMaterial';
import ComplexMeshMaterial from './materials/ComplexMeshMaterial';
Expand Down Expand Up @@ -45,6 +46,7 @@ const dgl = {
Mesh,
Sprite,
MultiSprite,
Shader,
ShaderProgram,
BasicMeshMaterial,
ComplexMeshMaterial,
Expand Down
9 changes: 5 additions & 4 deletions src/materials/Material.js
@@ -1,6 +1,8 @@
import definitions from './definitions';
import ShaderProgram from '../ShaderProgram';
import libConstants from '../libConstants';
import Shader from '../Shader';

import '../rendererPlugins/CommonPlugin';
import '../rendererPlugins/TransparentPlugin';

Expand Down Expand Up @@ -106,11 +108,10 @@ class Material {
}

this._shaderProgram = new ShaderProgram({
vertex: this._shader.vertex,
fragment: this._shader.fragment,
vertex: new Shader('vertex', this._shader.vertex, this._definitions),
fragment: new Shader('fragment', this._shader.fragment, this._definitions),
uniforms: this._uniforms,
attributes: this._attributes,
definitions: this._definitions
attributes: this._attributes
});

cachedPrograms[this._getCachedProgramKey()] = {
Expand Down
5 changes: 3 additions & 2 deletions src/rendererPlugins/MultiSpritePlugin.js
Expand Up @@ -3,6 +3,7 @@ import vertexShader from '../shaders/multiSprite.vert.js';
import ShaderProgram from '../ShaderProgram';
import RendererPlugin from '../RendererPlugin';
import Renderer from '../Renderer';
import Shader from '../Shader';
import libConstants from '../libConstants';

/**
Expand All @@ -15,8 +16,8 @@ class MultiSpritePlugin extends RendererPlugin {
this._renderer = renderer;

this._shaderProgram = new ShaderProgram({
vertex: vertexShader,
fragment: fragmentShader,
vertex: new Shader('vertex', vertexShader),
fragment: new Shader('fragment', fragmentShader),
uniforms: [
{name: 'uPCamera', type: 'mat4'},
{name: 'uHalfSize', type: '2f'},
Expand Down
5 changes: 3 additions & 2 deletions src/rendererPlugins/SpritePlugin.js
Expand Up @@ -4,6 +4,7 @@ import ShaderProgram from '../ShaderProgram';
import RendererPlugin from '../RendererPlugin';
import Geometry from '../Geometry';
import Renderer from '../Renderer';
import Shader from '../Shader';
import Buffer from '../Buffer';
import libConstants from '../libConstants';

Expand Down Expand Up @@ -36,8 +37,8 @@ class SpritePlugin extends RendererPlugin {
this._geometry.getBuffer('index').type = Buffer.ElementArrayBuffer;

this._shaderProgram = new ShaderProgram({
vertex: vertexShader,
fragment: fragmentShader,
vertex: new Shader('vertex', vertexShader),
fragment: new Shader('fragment', fragmentShader),
uniforms: [
{name: 'uPCamera', type: 'mat4'},
{name: 'uPosition', type: '3f'},
Expand Down
13 changes: 0 additions & 13 deletions test/ShaderProgram.spec.js
Expand Up @@ -24,12 +24,6 @@ describe('ShaderProgram', () => {
program = new ShaderProgram();
});

it('should create two shaders', () => {
const spy = sinon.spy(gl, 'createShader');
program.enable(gl);
assert.ok(spy.calledTwice);
});

it('should create program', () => {
const spy = sinon.spy(gl, 'createProgram');
program.enable(gl);
Expand All @@ -42,13 +36,6 @@ describe('ShaderProgram', () => {
assert.ok(spy.calledOnce);
});

it('shouldn\'t create shaders after first call', () => {
const spy = sinon.spy(gl, 'createShader');
program.enable(gl);
program.enable(gl);
assert.ok(spy.calledTwice);
});

it('shouldn\'t create program after first call', () => {
const spy = sinon.spy(gl, 'createProgram');
program.enable(gl);
Expand Down

0 comments on commit 2a6b0a7

Please sign in to comment.