Files
Kepler/engine/shader.js

1012 lines
20 KiB
JavaScript
Raw Permalink Normal View History

2025-11-17 17:18:43 +01:00
/**
* Kepler - Core
**/
import {vector2, vector3, matrix4 } from './math.js';
import sampler2D from './sampler2D.js';
import samplerCube from './samplerCube.js';
class uniform_template{
name;
value;
prepared = false;
}
/**
* Shader Object
* this class takes care of the shader management for you
*/
class shader {
program;
uniform_templates = new Array();
uniforms = new Array();
attributes = new Array();
url;
samplerId = 0;
libraryContent = false;
librarys = new Array();
pragmas = new Array();
rawShader;
programInfo;
compiled = false;
blend = 0;
/**
* set viewport
* @param {(viewport)} viewport
**/
setViewport( viewport ){
this.viewport = viewport;
this.gl = viewport.gl;
}
createFromFile( url ) {
this.url = url;
var shaderText = kepler.resources.loadTextFileSynchronous(url);
var splitShader = shaderText.split("// #keplerEngine - Split");
this.vertexShaderText = splitShader[0];
this.fragmentShaderText = splitShader[1];
//add pragma's
for(var c = 0; c<this.pragmas.length; c++) {
var currentPragma = this.pragmas[c];
switch (currentPragma.type) {
case "define":
var pragma = "#define " + currentPragma.name + " " + currentPragma.value + ' \n ';
this.fragmentShaderText = pragma + this.fragmentShaderText;
this.vertexShaderText = pragma + this.vertexShaderText;
break;
}
}
var allLines = this.fragmentShaderText.split("\n");
for(var b = 0; b<allLines.length; b++) {
var currentLine = allLines[b];
if( currentLine.includes("#include") ) {
console.log("include file", currentLine);
var fileName = currentLine.replace("#include",'').replace('"','').replace('"','').replace(' ','').replace(' ','').replace(' ','');
var fileContent = kepler.resources.loadTextFileSynchronous("shaders/"+fileName);
//this.fragmentShaderText = fileContent + this.fragmentShaderText;
allLines[b] = fileContent;
//console.log(allLines[b]);
}
}
this.fragmentShaderText = allLines.join("\n");
var shader_version_3 = this.fragmentShaderText.includes("#version 300 es");
if( shader_version_3 ) {
this.fragmentShaderText = this.fragmentShaderText.replace("#version 300 es",'');
this.fragmentShaderText = this.fragmentShaderText.replace("#version 300 es",'');
this.vertexShaderText = this.vertexShaderText.replace("#version 300 es",'');
this.vertexShaderText = this.vertexShaderText.replace("#version 300 es",'');
this.fragmentShaderText = "#version 300 es \n" + this.fragmentShaderText;
this.vertexShaderText = "#version 300 es \n" + this.vertexShaderText;
}
for (var i = 0; i < 10; i++) {
this.fragmentShaderText = this.fragmentShaderText.replace("#include",'//');
}
this.vertexShaderText = this.vertexShaderText.replace("#include",'//');
this.precompile();
}
getTypeName( index ) {
switch( index ) {
case 35676:
return "mat4";
break;
case 5126:
return "float";
break;
case 35665:
return "vec3";
break;
case 35678:
return "sampler2D";
break;
case 35680:
return "samplerCube";
break;
default:
return index;
}
}
extractUniforms() {
const numUniforms = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < numUniforms; ++i) {
const info = gl.getActiveUniform(this.program, i);
//console.log("name:", info.name, "type:", info.type, "size:", info.size, "typeName" );
info.typeName = this.getTypeName(info.type);
var uniform = {};
uniform.type = this.getTypeName(info.type);
uniform.name = info.name;
this.addUniform(uniform);
}
}
precompile() {
this.gl = gl;
this.isCompiled = true;
this.vertexShader = this.gl.createShader(this.gl.VERTEX_SHADER);
this.fragmentShader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
this.gl.shaderSource(this.vertexShader, this.vertexShaderText);
this.gl.compileShader(this.vertexShader);
this.gl.shaderSource(this.fragmentShader, this.fragmentShaderText);
this.gl.compileShader(this.fragmentShader);
this.program = this.gl.createProgram();
this.gl.attachShader(this.program, this.vertexShader);
this.gl.attachShader(this.program, this.fragmentShader);
if (!this.gl.getShaderParameter(this.vertexShader, this.gl.COMPILE_STATUS)) {
alert(this.gl.getShaderInfoLog(this.vertexShader));
return null;
}
if (!this.gl.getShaderParameter(this.fragmentShader, this.gl.COMPILE_STATUS)) {
alert(this.gl.getShaderInfoLog(this.fragmentShader));
return null;
}
this.gl.linkProgram(this.program);
if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
this.gl.useProgram(this.program);
const numAttribs = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES);
for (let i = 0; i < numAttribs; ++i) {
const info = gl.getActiveAttrib(this.program, i);
//console.log("attribute name:", info.name, "type:", info.type, "size:", info.size);
this.addAttribute(info.name);
}
this.extractUniforms();
for(var c = 0; c<this.attributes.length; c++) {
var attribute = this.attributes[c];
this.bindAttribute( attribute );
}
}
compile(){
this.bindUniforms();
this.compiled = true;
}
/**
* create shader library object
* @param {(String)} fileUrl of shader file.
**/
createLibraryFomFile( fileUrl ){
this.libraryContent = kepler.resources.loadTextFileSynchronous(fileUrl);
};
/**
* Add library to shader object
* @param {(shaderObject)} shader.
* @param {(int)} type (pixel shader = 0, vertex shader = 1).
**/
addLibrary( shader, type ){
var library = {};
library.content = shader.libraryContent;
library.type = type;
library.uniforms = shader.uniforms;
//this.pragmas = pragmas.concat(shader.pragmas);
this.librarys.push(library);
};
/**
* Define pragma's
* @param {(String)} name.
* @param {(string)} value.
**/
definePragma(name, value) {
var pragma = {};
pragma.type = "define";
pragma.name = name;
pragma.value = value;
this.pragmas.push(pragma);
};
/**
* Get uniform by name
* @param {(string)} name.
**/
getUniformByName( name ) {
var uniforms = this.uniforms;
for(var c = 0; c < uniforms.length; c++) {
var uniform = uniforms[c];
if( uniform.name == name ){
return uniform.uniformLocation;
}
}
return false;
console.log("could not locate buffer :"+name);
};
asddsaas = 0;
/**
* Update uniform variable
* @param {(uniformObject)} uniform.
**/
updateUniform(uniform) {
var uniformLocation = uniform.uniformLocation;
var value = uniform.value;
switch(uniform.type) {
case "sampler2D":
var sampler = value;
var type = sampler.type;
//if(asddsaas==0)
//console.log(sampler);
this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id );
//if transparent
if (!sampler.useAlpha) {
this.gl.disable(this.gl.BLEND);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.enable(this.gl.DEPTH_TEST);
} else {
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
this.gl.enable(this.gl.BLEND);
this.gl.disable(this.gl.DEPTH_TEST);
}
this.gl.disable(this.gl.BLEND);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.enable(this.gl.DEPTH_TEST);
//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
//this.gl.enable(this.gl.BLEND);
//for(var b = 0; b<sampler.textures.length;b++) {
var texture = sampler.getTexture();
var glTexture = texture.glTexture;
this.gl.bindTexture(sampler.target, glTexture);
this.gl.uniform1i(uniformLocation, sampler.id);
//}
//this.gl.bindTexture(this.gl.TEXTURE_2D, null);
break;
case "samplerCube":
//console.log("hiiier");
var sampler = value;
//if(asddsaas==0) {
// console.log(sampler);
// asddsaas++;
//}
var type = sampler.type;
this.gl.activeTexture(this.gl.TEXTURE0 + sampler.id);
//if transparent
if (!sampler.useAlpha) {
this.gl.disable(this.gl.BLEND);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.enable(this.gl.DEPTH_TEST);
} else {
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
this.gl.enable(this.gl.BLEND);
this.gl.disable(this.gl.DEPTH_TEST);
}
this.gl.disable(this.gl.BLEND);
this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.enable(this.gl.DEPTH_TEST);
//this.gl.pixelStorei(this.gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
//this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA);
//this.gl.enable(this.gl.BLEND);
//for(var b = 0; b<sampler.textures.length;b++) {
var texture = sampler.cubeTexture;
var glTexture = texture;
this.gl.bindTexture(sampler.target, glTexture);
this.gl.uniform1i(uniformLocation, sampler.id);
//}
//this.gl.bindTexture(this.gl.TEXTURE_2D, null);
break;
case "float":
this.gl.uniform1f(uniformLocation, value);
break;
case "vec2":
this.gl.uniform2f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]) );
break;
case "vec3":
this.gl.uniform3f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2]) );
break;
case "vec4":
this.gl.uniform4f(uniformLocation, parseFloat(value[0]), parseFloat(value[1]), parseFloat(value[2]), parseFloat(value[3]));
break;
case "mat2":
this.gl.uniformMatrix2fv(uniformLocation, false, matrix2.getMatrixElements(value));
break;
case "mat3":
this.gl.uniformMatrix3fv(uniformLocation, false, matrix3.getMatrixElements(value));
break;
case "mat4":
this.gl.uniformMatrix4fv(uniformLocation, false, matrix4.getMatrixElements(value));
break;
case "int":
break;
case "ivec2":
break;
case "array":
var arrayType = uniform.arrayType;
//console.log(arrayType);
switch(arrayType) {
case "float":
this.gl.uniform1fv(uniformLocation, value );
break;
case "vec2":
this.gl.uniform2fv(uniformLocation, value );
break;
case "vec3":
var flat = [];
for(var c = 0; c<value.length; c++) {
var v = value[c];
flat.push(v[0], v[1], v[2]);
}
this.gl.uniform3fv( uniformLocation, flat );
break;
case "vec4":
var flat = [];
for(var c = 0; c<value.length; c++) {
var v = value[c];
flat.push(v[0], v[1], v[2], v[3]);
}
this.gl.uniform4fv( uniformLocation, flat );
break;
}
break;
}
}
setUniform( name, value ) {
//console.log(name, value);
for(var c = 0; c < this.uniforms.length; c++) {
if( this.uniforms[c].name == name ) {
this.uniforms[c].value = value;
if(this.compiled && this.uniforms[c].uniformLocation){
this.updateUniform( this.uniforms[c] );
}
}
}
}
/**
* Set uniform variable
* @param {(String)} name.
* @param {(String)} value.
* @param {(boolean)} noError.
**/
/*
setUniform(name, value, update) {
var a = 0;
var uniforms = this.uniforms;
for(var c = 0; c < uniforms.length; c++) {
if(uniforms[c].name == name) {
if(uniforms[c].type == "sampler2D") {
//console.log(value);
value.bind(this);
}
if(uniforms[c].type == "samplerCube") {
//console.log(value);
value.bind(this);
}
uniforms[c].value = value;
if(update)
this.updateUniform(uniforms[c]);
a++;
}
}
if(this.libraryContent) {
var uniform = {};
uniform.name = name;
uniform.value = value;
uniforms.push(uniform)
a++;
}
if(a == 0)
console.log('uniform '+name+' not found in shader ', this.url, this);
}
*/
/**
* Set uniform variable
* @param {(String)} name.
* @param {(String)} value.
* @param {(boolean)} noError.
**/
addUniform( uniform ) {
uniform.value = this.createEmptyValue(uniform);
uniform.objectType = 'uniform';
this.uniforms.push( uniform );
}
/**
* Set uniform variable
* @param {(String)} name.
* @param {(String)} value.
* @param {(boolean)} noError.
**/
bindUniforms() {
//console.log("bind uniforms");
var oldUniforms = this.uniforms;
this.uniforms = [];
this.gl.useProgram(this.program);
for(var c = 0; c<oldUniforms.length; c++) {
var uniform = oldUniforms[c];
//console.log("huh", uniform);
//this.gl.useProgram(this.program);
//if(!uniform.compiled) {
//console.log("bind uniform", uniform.name);
uniform.uniformLocation = this.gl.getUniformLocation(this.program, uniform.name);
//console.log("uniformLocation", uniform.name, uniform.uniformLocation);
if(!uniform.value)
uniform.value = this.createEmptyValue(uniform);
uniform.objectType = 'uniform';
if(uniform.type == "sampler2D") {
var originalSampler = uniform.value;
var sampler = new sampler2D();
//console.log('uniform loading', uniform);
var texture = originalSampler.texture;
if(!texture.loaded && !originalSampler.isFramebuffer && texture.dataType != "int") {
sampler.texture = kepler.resources.getLoadedTexture( texture.name, this.viewport );
}
if(originalSampler.isFramebuffer)
{
sampler.datatype = originalSampler.datatype;
sampler.format = originalSampler.format;
sampler.internalFormat = originalSampler.internalFormat;
sampler.filter = originalSampler.filter;
sampler.texture = originalSampler.texture;
}
if(texture.dataType == "int") {
sampler.texture = originalSampler.texture;
sampler.texture.setViewport( this.viewport );
}
//sampler.samplerID = ++this.samplerId;
//console.log("new texture", kepler.resources, texture.name, sampler);
sampler.setViewport( this.viewport );
if(!sampler.binded) {
}
sampler.bind(this);
//console.log("new sampler", sampler);
uniform.value = sampler;
}
if(uniform.type == "samplerCube") {
var sampler = uniform.value;
sampler.setViewport( this.viewport );
//sampler.samplerID = ++this.samplerId;
var textures = sampler.textures;
for(var b = 0; b<textures.length;b++) {
//console.log("cube texture", textures[c]);
//console.log(sampler.textures[c].texture.name);
var texture = textures[b];
if(!texture.loaded) {
sampler.textures[b].texture = kepler.resources.getLoadedTexture(sampler.textures[b].texture.name, this.viewport );
}
}
if(!sampler.binded) {
}
sampler.bind(this);
uniform.value = sampler;
}
uniform.compiled = true;
var exist = this.getUniformByName(uniform.name);
if(uniform.uniformLocation != null && !exist){
//console.log("add", uniform.name);
this.uniforms.push(uniform);
//console.log("update", uniform.name);
this.updateUniform(uniform);
}
}
//if(uniform.uniformLocation)
// this.uniforms.push(uniform);
//}
}
/**
* Create a empty object based on the type of the uniform object
* @param {(uniformObject)} uniform.
**/
createEmptyValue(uniform) {
switch(uniform.type) {
case "sampler2D":
var def = kepler.resources.getTexture("default");
var sampler = new sampler2D( kepler );
sampler.addTexture(def);
return sampler;
break;
case "samplerCube":
var dataArray = [];
var width = 512;
var height = 512;
for( var y = 0; y < height; y++ )
{
for( var x = 0; x < width; x++ )
{
dataArray.push(x / width);
dataArray.push( y / width);
dataArray.push( x / width);
dataArray.push( y * x / width);
}
}
var def = kepler.textureFromArray(dataArray, width, height, true);
//var def = kepler.resources.getTexture("default");
var sampler = new samplerCube( );
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_X);
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_X);
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_Y);
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y);
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_POSITIVE_Z);
sampler.addTexture(def, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z);
return sampler;
break;
case "float":
return 0.0;
break;
case "vec2":
return new vector2(0,0);
break;
case "vec3":
return new vector3(0,0,0);
break;
case "vec4":
return new vector4(0,0,0,0);
break;
case "mat2":
return matrix2.identity();
break;
case "mat3":
return matrix3.identity();
break;
case "mat4":
return matrix4.identity();
break;
case "int":
return 0;
break;
case "ivec2":
break;
case "array":
/*
var arrayType = uniform.arrayType;
switch(arrayType) {
case "vec2":
//this.gl.uniform2fv(uniformLocation, value );
break;
case "vec3":
//this.gl.uniform3fv(uniformLocation, value );
break;
case "vec4":
//this.gl.uniform4fv(uniformLocation, value );
break;
}
*/
break;
}
}
/**
* Update shader
**/
update(viewport) {
if( !this.compiled ) {
this.setViewport( viewport );
this.gl.useProgram(this.program);
this.compile();
}
this.gl.useProgram(this.program);
var uniforms = this.uniforms;
for(var c = 0; c < uniforms.length; c++) {
var uniform = uniforms[c];
this.updateUniform(uniform);
}
}
/**
* Get Attribute by name
* @param {(String)} name.
**/
getAttributeByName(name) {
var attributes = this.attributes;
for(var c = 0; c < attributes.length; c++) {
var attribute = attributes[c];
if(attribute.name == name)
return attribute.uniformLocation;
}
console.log("could not locate buffer :"+name);
}
addAttribute( name ) {
var attr = {};
attr.name = name;
this.attributes.push( attr );
}
/**
* add attribute to shader.
* @param {(String)} name.
**/
bindAttribute( attr ) {
//var attr = {};
this.gl.useProgram(this.program);
//console.log('this.gl.getAttribLocation()', this.program, name);
//attr.name = name;
//console.log("bind", this.program, attr.name);
attr.uniformLocation = this.gl.getAttribLocation(this.program, attr.name);
if(attr.uniformLocation != null) {
this.gl.enableVertexAttribArray( attr.uniformLocation );
}
if(typeof(attr.uniformLocation)!='number')
console.log("attribute '"+attr.name+"' Does not exist in shader ",this);
//else
// console.log("added attribute",attr);
//this.attributes.push( attr );
}
}
export {shader as default};
/**
* number padding
* @param {(int)} number.
**/
function pad2(number) {
return (number < 10 ? '0' : '') + number
}
/**
* Contains
* @param {(object)} obj.
**/
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
};
/**
* Between
* @param {(string)} prefix.
* @param {(string)} suffix.
**/
String.prototype.between = function(prefix, suffix) {
s = this;
var i = s.indexOf(prefix);
if (i >= 0) {
s = s.substring(i + prefix.length);
}
else {
return '';
}
if (suffix) {
i = s.indexOf(suffix);
if (i >= 0) {
s = s.substring(0, i);
}
else {
return '';
}
}
return s;
};
/**
* Chech if object is array
* @param {(obj)} object.
**/
function isArray(obj) {
return obj.constructor == Array;
}
/**
* add string to string at a particular index
* @param {(String)} src.
* @param {(int)} index.
* @param {(String)} str.
**/
function insertAt(src, index, str) {
return src.substr(0, index) + str + src.substr(index)
}
var ttt= 0;
var samplerId = 0;