About

Welcome to Shader Hub. This site is designed for 3D Artists and GLSL Shader programmers as well as anyone who just wants to play around with computer graphics and WebGL. It is essentially a 3D repository where artists can upload their work and view it in 3D using WebGL. Note that you must have WebGL enabled in your browser to use this site.

Below is an overview of the objectives of Shader Hub although currently not all these objectives have been met as it is a work in progress.

Mission Statement

Getting Started

Video Tutorials

For those of you that like me prefer to follow a youtube video than follow written instructions I have prepared some video tutorials to help you get up and running.


Tutorial 1: How to set up the Blender plugin for Shaderhub - download the plugin here

Tutorial 2: How to upload your 3D Mesh to Shaderhub and configure the vertex/fragment shaders

Step By Step Instructions

Although relatively easy to use, to get your 3D live online it is not simply a matter of uploading your 3D and leaving at that. Because this site is designed for Shader programmers as well as Artists it is necessary to also at minimum configure some shader parameters which essentially tells the shader which bits of data you want to pass to which parameters. Users who have written GLSL shaders will probably want to enhance the shader code to implement extra effects. Other users who are not familiar with programming may find it a bit complicated to get started but by following the tutorial you should be able to get your work up and visible pretty quickly.

Upload your 3D

  1. Make sure your browser supports webGL and that it is enabled - check here
  2. Register and log in to your account - It is completely free, but you will have to verify your email address.
  3. Navigate to your profile. To do this, open the slide out menu and select Profile. You will notice there is a section labeled secret with a GUID after it. This number is your secret code which identifies you when sending up content - You should not share this number with others! Make a note of it as you will need it later.
  4. Make sure you have a copy of Blender 2.69 running with your 3D content loaded. Other versions may work but because blender is constantly being developed and changing the python script it supports is also likely to change from time to time.
  5. You now have two options, install the blender plugin which you can download here or go to Blender open a python editor window and paste the following code into it:
    import json
    import requests
    
    def roundList(l, n):
        return list(map(lambda x: round(x, n), l))
    
    def exportMesh(obj_dict, obj, prefix):
        bpy.context.scene.objects.active
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.select_all(action='SELECT')
        bpy.ops.mesh.quads_convert_to_tris()
        bpy.ops.object.mode_set(mode='OBJECT')
        mesh = bpy.context.active_object.data
        mesh.update(True, True)
        vertex_list = []
        normal_list = []
        smooth = False
        for face in mesh.tessfaces:
            if face.use_smooth:
                smooth = True
            for index in face.vertices:
                vert = mesh.vertices[index]
                vertex_list.append(vert.co[0])
                vertex_list.append(vert.co[1])
                vertex_list.append(vert.co[2])
                if smooth:
                    normal_list.append(vert.normal[0])
                    normal_list.append(vert.normal[1])
                    normal_list.append(vert.normal[2])
                else:
                    normal_list.append(face.normal[0])
                    normal_list.append(face.normal[1])
                    normal_list.append(face.normal[2])
        obj_dict[prefix+'_vertices'] = roundList(vertex_list,4)
        obj_dict[prefix+'_normals'] = roundList(normal_list,4)
        useTexture = False
        if len(mesh.tessface_uv_textures) > 0:
            uv_list = []
            active_uv_layer = mesh.tessface_uv_textures.active
            if(active_uv_layer):
                useTexture = True
                for face_uvmap in mesh.tessface_uv_textures:
                    for uvmap in face_uvmap.data:
                        uv_list.append(uvmap.uv[0][0])
                        uv_list.append(uvmap.uv[0][1])
                        uv_list.append(uvmap.uv[1][0])
                        uv_list.append(uvmap.uv[1][1])
                        uv_list.append(uvmap.uv[2][0])
                        uv_list.append(uvmap.uv[2][1])
                obj_dict[prefix+'_'+face_uvmap.name+'_uvs'] = roundList(uv_list,6)
        obj_dict = exportShader(obj_dict, prefix+'_Shader', useTexture)
        return obj_dict
            
    def exportMatrix(obj_dict, obj, prefix):
        w = []
        l = []
        for i in range(0, 4):
            for j in range(0, 4):
                w.append(obj.matrix_world[i][j])
                l.append(obj.matrix_local[i][j])
        obj_dict[prefix+'_local'] = l
        obj_dict[prefix+'_world'] = w
        return obj_dict
    
    def exportShader(obj_dict, prefix, useTexture):
        if useTexture:
            obj_dict[prefix+'_frag'] = "\n\
    	precision mediump float;\n\
    	varying vec3 vTransformedNormal;\n\
    	varying vec4 vPosition;\n\
    	varying vec2 vTextureCoord;\n\
    	uniform vec3 uAmbientColor;\n\
    	uniform vec3 uPointLightingLocation;\n\
    	uniform vec3 uPointLightingColor;\n\
    	uniform sampler2D uSampler;\n\
    	void main(void) {\n\
    		vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);\n\
    		float directionalLightWeighting = max(dot(normalize(vTransformedNormal), lightDirection), 0.0);\n\
    		vec3 lightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;\n\
    		vec4 fragmentColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));\n\
    		gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);\n\
    	}"
            obj_dict[prefix+'_vert'] = "\n\
        attribute vec3 aVertexPosition;\n\
        attribute vec3 aVertexNormal;\n\
    	attribute vec2 aTextureCoord;\n\
    	uniform mat4 uMVMatrix;\n\
        uniform mat4 uPMatrix;\n\
        uniform mat3 uNMatrix;\n\
        varying vec2 vTextureCoord;\n\
        varying vec3 vTransformedNormal;\n\
        varying vec4 vPosition;\n\
        void main(void) {\n\
            vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);\n\
            gl_Position = uPMatrix * vPosition;\n\
            vTransformedNormal = uNMatrix * aVertexNormal;\n\
    		vTextureCoord = aTextureCoord;\n\
        }"
        else:
            obj_dict[prefix+'_frag'] = "\n\
        precision mediump float;\n\
    	varying vec3 vTransformedNormal;\n\
    	varying vec4 vPosition;\n\
    	uniform vec3 uAmbientColor;\n\
    	uniform vec3 uPointLightingLocation;\n\
    	uniform vec3 uPointLightingColor;\n\
    	void main(void) {\n\
    		vec3 lightDirection = normalize(uPointLightingLocation - vPosition.xyz);\n\
    		float directionalLightWeighting = max(dot(normalize(vTransformedNormal), lightDirection), 0.0);\n\
    		vec3 lightWeighting = uAmbientColor + uPointLightingColor * directionalLightWeighting;\n\
    		vec4 fragmentColor = vec4(1.0, 1.0, 1.0, 1.0);\n\
    		gl_FragColor = vec4(fragmentColor.rgb * lightWeighting, fragmentColor.a);\n\
    	}"
            obj_dict[prefix+'_vert'] = "\n\
        attribute vec3 aVertexPosition;\n\
        attribute vec3 aVertexNormal;\n\
        uniform mat4 uMVMatrix;\n\
        uniform mat4 uPMatrix;\n\
        uniform mat3 uNMatrix;\n\
        varying vec3 vTransformedNormal;\n\
        varying vec4 vPosition;\n\
        void main(void) {\n\
            vPosition = uMVMatrix * vec4(aVertexPosition, 1.0);\n\
            gl_Position = uPMatrix * vPosition;\n\
            vTransformedNormal = uNMatrix * aVertexNormal;\n\
        }"
        return obj_dict
        
    def exportSelectedObjects():
        i = 1
        obj_dict = {}
        for obj in bpy.context.selected_objects:
            if obj.type == "MESH":
                prefix = ("Mesh_%03d_" % i) + obj.name
                obj_dict = exportMesh(obj_dict, obj, prefix)
            prefix = ("Matrix_%03d_" % i) + obj.name
            obj_dict = exportMatrix(obj_dict, obj, prefix)
            i = i + 1
        return obj_dict
    
    scene_file = {
                'file_name' : '*** My3DFile ***',
                'file_info' : '*** Add description here ***',
                'secret' : '*** Put secret GUID here ***',
                'op' : 'overwrite'}
    
    scene_file['data']=exportSelectedObjects()
    
    url = "http://igsite-shaderdesigner.rhcloud.com/datastore/uploadjson"
    headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
    
    r = requests.post(url, data=json.dumps(scene_file), headers=headers)
    
    This is the script we use to upload 3D content from Blender to the website!
  6. You will notice at the bottom there is a section that look like this:
    scene_file = {
                'file_name' : '*** My3DFile ***',
                'file_info' : '*** Add description here ***',
                'secret' : '*** Put secret GUID here ***',
                'op' : 'overwrite'}
    
    Replace *** My3DFile *** with a file name which will appear in your file list online.
    Replace *** Add description here *** with a description of your file and please credit the artist if it is not you.
    Replace *** Put secret GUID here *** with the secret GUID that we found earlier.
  7. This script requires a module called requests.py to be installed for blenders version of python. Currently this module is not part of blender but is needed to send data to the website, note also that setting it up for a global version of python is not enough as Blender will need to know how to access it. Either install the model and copy it into blenders python modules or make sure you have a path variable set up so that blender can find it.

    Setting up the requests module is beyond the scope of this tutorial, but these links should help:

    Note that you can test it by typing:
    import requests
    Into the interactive python console in blender. If it worked you should not see the following error:
    ImportError: No module named 'requests'

  8. Ensure that the object(s) you want to send up are selected and run the script. Assuming everything went to plan your file should now be stored online.

Upload Textures

  1. You must upload any textures used by your 3D manually, to do this, from your profile, click on files and locate the file you just uploaded. You should see three tabs. The first called attributes should contain the key's for the various bits of data in your 3D model file. The second tab is for managing any associated textures and the final tab will be for downloading content on mobile devices via QR code but the mobile applications haven't been built yet. They are on my ToDo list.
  2. Open the textures tab and name and upload your textures from there. Note that I haven't written delete yet, so you cannot remove textures once they are uploaded currently. Ensure that you give them a sensible names as you will need to access your textures on the next stage.

Configure your Shaders

When you uploaded your file from blender it sent with it some template shaders which should be enough to get you started. If the model has uv coordinates it will have sent the textured version of the fragment/vertex shaders. Otherwise it will send a simpler shader that does not include texture mapping.

  1. Click add program to create a new shader.
  2. Choose and/or edit your vertex and fragment shaders.
  3. Hit the compile button.
  4. If there were no errors you should now be able to configure your parameters. In the first section is listed all the attributes going into your shader, these would be things like normals, vertex positions and uv coordinates. From the select boxes bind the attributes to the associated data binding that belong to your model.
  5. Next you will see a list of uniform values from your shader. These also need to be configured, if a suitable datatype was uploaded from blender then you can just select the binding, otherwise it must be configured manually.
  6. To define data manually, you can select the Define button, here you can set up initial values for your uniform parameters.
  7. You will also notice a button called Apply Operations. This tells the system to apply certain operations to your scene. Some require data to have been defined previously either via the drop down or the manual define, others do not require data but generate data for you.

    The configuration process is a little complicated if you don't know what the parameters are for, but there are some useful operations you can apply:

    For your projection matrix you can use the operations, Perspective or Orthographic. The easiest way to set up your normal matrix is to select the objects Local matrix transform rather than the world matrix transform and apply the operations Transpose. You can also use the Inverse and Transpose operations together on your world matrix, but I don't think you need to do that because the inverse just takes your model back to local coordinates anyway.

    For your model view parameter I would set it to the world matrix from the select box and then add the CameraLookAt operation which will allow you to navigate the camera in the scene (only via sliders at the moment - may add keyboard/mouse navigation later?)

    Another operation you will find useful is Range which just applies to scalar values and vectors. This is for the controls on your HUD when you render the scene. It allows you to modify your uniform values with sliders in real time. It has three parameters, min, max and step - These define the minimum value for your uniform parameter, the maximum number and the size of the step on the slider. You can use this to for instance change colour settings or light settings etc. What you decide to bind to it really depends on the uniforms in your shader and what you want to expose to the user viewing your model.

  8. When you are content that you have configured your shader parameters. Navigate back to your file and the select the Render link. If everything was configured correctly you should see your model rendered in 3D. You can move the sliders around to change your camera/perspective or orthographic parameters and any others uniforms you exposed to the HUD using the Range operation.

Note: the take snapshot button allows you to create a thumbnail for your model and also share a link to your work on social networks.

I'm sorry but there is no FAQ or Forum sections yet, but feel free to contact me via my blog if you have any questions or difficulties.

If you want to learn more about webGL and GLSL shader programming, a good place to start is: Learning Web GL.