Skip to content

Commit 1fec12c

Browse files
committed
Added initial documentation
1 parent 9687b67 commit 1fec12c

File tree

99 files changed

+3601
-14
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+3601
-14
lines changed

.docs/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
###############
2+
# folder #
3+
###############
4+
/**/DROP/
5+
/**/TEMP/
6+
/**/packages/
7+
/**/bin/
8+
/**/obj/
9+
_site
10+
.idea/
11+
*.meta

.docs/api/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
###############
2+
# temp file #
3+
###############
4+
*.yml
5+
.manifest

.docs/api/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Welcome to the API Section
2+
3+
Here you will find the documentation of each publicly available classes within the API.

.docs/docfx.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"metadata": [],
3+
"build": {
4+
"content": [],
5+
"resource": [
6+
{
7+
"files": [
8+
"images/**",
9+
"logo.png",
10+
"favicon.ico"
11+
]
12+
}
13+
],
14+
"overwrite": [
15+
{
16+
"files": [
17+
"apidoc/**.md"
18+
],
19+
"exclude": [
20+
"obj/**",
21+
"_site/**"
22+
]
23+
}
24+
],
25+
"dest": "_site",
26+
"xref": [ "https://normanderwan.github.io/UnityXrefMaps/xrefmap.yml" ],
27+
"xrefService": [ "https://xref.docs.microsoft.com/query?uid={uid}" ],
28+
"postProcessors": [ "ExtractSearchIndex" ],
29+
"globalMetadata": {
30+
"_appTitle": "Modular Shader System Documentation",
31+
"_appFooter": "<span>Copyright © VRLabs.<br>Generated by <strong>DocFX</strong></span>",
32+
"_disableContribution": true,
33+
"_enableSearch": "true"
34+
},
35+
"fileMetadataFiles": [],
36+
"template": [
37+
"default",
38+
"templates/darkfx",
39+
"templates/memberpage/content"
40+
],
41+
"markdownEngineName": "markdig",
42+
"noLangKeyword": false,
43+
"keepFileLink": false,
44+
"cleanupCacheHistory": false,
45+
"sitemap": {
46+
"baseUrl": "https://mss.vrlabs.dev",
47+
"priority": 0.4,
48+
"changefreq": "monthly"
49+
}
50+
}
51+
}

.docs/favicon.ico

14.7 KB
Binary file not shown.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
uid: adv-EmbeddingLibrary
3+
title: Embedding The Library
4+
---
5+
6+
# Embedding The Library
7+
8+
Embedding the library is the process of making a functional copy of the library for your exclusive use, making you able to export a unity package of your modular shader without worrying about the end user having to download the modular shader system by themselves, and also gives you control over what version of the modular shader system the shader is shipped with.
9+
10+
If you're planning to make a shader to publish it's important to do this as fist step, since there are going to be some differences applied to the embedded library compared to the base one.
11+
12+
Luckily for you there's an editor window dedicated to this, and can be found in `VRLabs > Modular Shader > Embed Library`.
13+
14+
## Embed Library Window
15+
16+
![window](/images/docs/AdvancedTopics/1.png)
17+
18+
The window contains some fields that are filled with the default values. Most of these values need to be changed based on your needs.
19+
20+
- **Namespace:** this will be the namespace of the embedded library, it has to differ from `VRLabs` since this one is already there, and it would cause compilation errors. The end namespace will always be `*YourInput*.ModularShaderSystem` (a preview is visible in the window)
21+
- **Default variable keyword:** is the keyword used by default when no keywords are provided for variables. You can change it to whatever you want, or keep it like that.
22+
- **Default code keyword:** is the keyword used by default when no keywords are provided for function code implementation. You can change it to whatever you want, or keep it like that.
23+
- **Default properties keyword:** is the keyword used as an entry point for templates that target the property block when that option is enabled. You can change it to whatever you want, or keep it like that.
24+
- **Resource folder:** it will be the name used for the resource folder of the library, it has to be different from the default value to avoid having collisions with the default library resources, since otherwise when those resources are used the default library ones may be loaded instead if both the embedded and original library are in the project, which could cause issues if the 2 libraries are of different versions with breaking changes between the 2 of them.
25+
- **Template extension:** extension for the template file. It has to be different from the default value, to avoid collisions with the scripted importer of the 2 versions.
26+
- **Collection extension:** extension for the template collection file. It has to be different from the default value, to avoid collisions with the scripted importer of the 2 versions.
27+
- **Editor window menu path:** path to the editor windows in the menu, has to be different from the default one to avoid collisions with the base library, which would end up to being able to only have the options for 1 of the 2 libraries.
28+
- **Create asset menu path:** path to the create asset menu in the menu, has to be different from the default one to avoid collisions with the base library, which would end up to being able to only have the options for 1 of the 2 libraries.
29+
30+
> [!IMPORTANT]
31+
> You should keep an eye to what public shaders using the system use for these options, since you also have to not collide with them.
32+
33+
After setting all those options for the first time, it's a good idea to save them to a file, so that the next times you have to update the embedded library you can just load the options from the file.
34+
35+
Now it's time to embed the library by clicking `Embed`. It will prompt you to select a folder to where to put the library. The folder must be a folder called `Editor` since everything needs to be an editor script. Once done the editor will copy the library with the modifications declared and save it under a folder called `ModularShaderSystem` inside the selected editor folder.
36+
37+
From here you can use the embedded library directly to make your own shader. This has also the advantage that since you now have your own file extensions for templates and collections, they won't get mixed up with other's people modular shader templates.
38+
39+
40+
41+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---
2+
uid: adv-GeneratorDive
3+
title: Modular Shader Generator Deep Dive
4+
---
5+
6+
# Modular Shader Generator Deep Dive
7+
8+
The modular shader generator is fairly simple to use, you just call the [GenerateShader](xref:VRLabs.ModularShaderSystem.ShaderGenerator.GenerateShader) method, pass in the destination folder path, the modular shader, and you're done.
9+
10+
But it may be useful to know what happens underneath and talk about the generation steps.
11+
12+
First of all, the [GenerateShader](xref:VRLabs.ModularShaderSystem.ShaderGenerator.GenerateShader) is a wrapper that sets up one or more [ShaderContext](xref:VRLabs.ModularShaderSystem.ShaderGenerator.ShaderContext) objects, and these objects are what actually generate the shader.
13+
This separation is done so that the generator is unified in all use cases, for example in the library it's used for both generating the shader and generating optimised shaders.
14+
15+
## GenerateShader method
16+
17+
In the case of the [GenerateShader](xref:VRLabs.ModularShaderSystem.ShaderGenerator.GenerateShader) method, it first retrieves and reloads all the used template assets,
18+
then it evaluates all the possible variants combinations the modular shader has (as a reminder, variants are those templates that to be able to have the module toggled on and off, need to have the code actually removed, ending up with multiple shader files),
19+
after that it generates the PropertyBlock string, which contains all properties declared in all modules (if you have the shader setup to use templates for properties, only the ones in the dedicated template in the modular shader asset is included here, the rest will be handled later with the other templates).
20+
21+
The PropertyBlock string is generated outside of the shader context here cause they will all share the same properties, so you only need to generate it once.
22+
23+
> [!NOTE]
24+
> in an ideal world reloading all template assets needed should not be done since you should be able to just take the reference from the modular shader and shader module assets, but in some specific situations (specifically when you first import unitypackages containing a modular shader, up until editor restart) that returns empty assets instead of our templates, so we just reload them for the generation process
25+
26+
27+
Now for each variant combination a ShaderContext is generated, by passing all its relevant informations.
28+
29+
Now that we have a list of contexts to run, we will just process them all in parallel, since everything that a context does is manipulating strings, we aren't limited to do it inside the main thread. This speeds up a lot the generation of shaders that have a lot of variant combinations.
30+
31+
After all the contexts are done, we tell unity that we are starting to edit assets, write down files with the result of each context, and then tell unity we finished editing, so that it can import the newly generated shaders.
32+
33+
To finish it off, we load all the newly generated shaders and add a reference to them in the modular shader asset, this way the modular shader always contains a reference to the last shaders that were generated with it.
34+
35+
## The ShaderContext
36+
37+
As we previously said, a [ShaderContext](xref:VRLabs.ModularShaderSystem.ShaderGenerator.ShaderContext) takes care of generating a single shader file using the informations it has been given by calling its [GenerateShader](xref:VRLabs.ModularShaderSystem.ShaderGenerator.ShaderContext.GenerateShader) method.
38+
39+
First it generates the name for both the file and the shader path in the material's shader selector, after that it generates the property section of the shader by using the provided property block or by generating its own if it's empty (in this case it makes the assumption that it's doing it for optimised shaders and does not include module's Enable properties).
40+
41+
### Adding templates
42+
43+
Then it's time to generate the SubShader block by applying the templates. They get listed from modules and then reordered by queue (this should keep the templates with the same queue ordered by position of their relative modules).
44+
45+
Then for each template there's a check to see if the template is a toggleable template that doesn't need a variant, in which case the template code gets enclosed by an `if` check (in optimised shaders this would never happen since the module would not be even in the list of used modules for the context generation).
46+
47+
After that it checks for the presence of any `internal keyword`, and each found one gets replaced with a runtime generated one that is dependent on the module id and original internal keyword. This is to assure that internal keywords that are in multiple modules don't actually get used by different modules from the one it was intended for.
48+
49+
> [!NOTE]
50+
> the instanced internal keywords are stored in a dictionary where the key is a combination of module id and original internal keyword, so that it can be easily retrieved when we search an internal keyword for a specific template in a module.
51+
52+
Now it's time to add the template to the main code by finding the indexes of the selected keywords (or instanced internal keywords), and inserting the template string (with the mentioned modifications) in each of these indexes, from the last one to the first one.
53+
54+
> [!NOTE]
55+
> We go from last to first index because if we went the other way around after the first index is used the others would not be valid, since the keywords positions have shifted indexes by the amount of characters equivalent to the lenght of the inserted template.
56+
>
57+
> We could have just taken that into account and also shifter our indexes after each iteration, but it's just simpler to start from the last and go backwards.
58+
59+
Once all templates have been dealt with, all the instanced internal keywords get removed, since they're not going to be used anymore.
60+
61+
### Adding functions
62+
63+
Now it's time to add all the functions declared in modules, which is a bit more involved process since there are multiple things to keep track of.
64+
65+
Fist we list all the functions that have to be added, then we start by adding all the variables in their respective keywords.
66+
This is done by looping each function, looking for which variables it declares, check which keywords they're supposed to go in, and then adding them to the respective list (these lists are contained in a dictionary where the key is the keyword they should go in).
67+
68+
iterating all functions for their variables, for each list a string with the variable declarations is generated (duplicates of variables get removed here), and then each string is added to the shader code with the same logic templates did (minus the internal keywords that are not here anymore).
69+
70+
Now that variables are dealt with, it's time for the functions themselves. Their order depends first on where they declare to go (keyword or other functions), then by their queue.
71+
Due to that, functions that go to keywords are the first to be looked at, since they are the root of the call chain. Therefore we look at all keywords that are being used by functions, and for each keyword we list all functions that go there, and order them by queue. Just like templates, we check if the function needs to be able to be disabled and in that case we enclose the call with an `if` check, and then append the string to the call sequence.
72+
73+
Before cycling to the next function in the list, we need to check if the function has other functions that declare to be appended right after it. This is pretty much the same process done with these top level functions, but using the function name instead of a keyword, so the entire process can be repeated recursively. And after that the next function in the list is evaluated and the process repeats.
74+
75+
At the end of the call sequence evaluation of each keyword, the entire call sequence string gets appended to each keywords, with the same logic already used for templates and variables.
76+
77+
During the evaluation process functions also get reordered into a list where the order is dependent on when a function has been used by the sequence, and this is used now to write down the actual function code implementation stored in template assets.
78+
For each function in the reordered list its code template string is taken and added to the relative keyword (in case of no keywords declared, the default `DEFAULT_CODE` keyword is used).
79+
80+
> [!NOTE]
81+
> the code strings are stored in a dictionary of StringBuilders first, and then added to the main code by keywords later on, so that the final insertion to the keywords is done only once per keyword.
82+
83+
During this process is also made sure that a code implementation template is not added more than once per keyword, to avoid code duplication that would make the shader fail to compile.
84+
85+
### Final steps
86+
87+
Now that functions have been taken of, there's some last things to take care of.
88+
First is to add the defined `CustomEditor` so that is used the inspector that has been defined in the modular shader asset, then a custom `PostGeneration` action is called.
89+
This action can be passed to the shader context to have custom code run at this step, this could be useful in case someone needs to do some edits to the shader code before it is finalized.
90+
91+
> [!TIP]
92+
> You can still look for keywords at this stage.
93+
94+
After that all the keywords used up to now get removed from the final shader code, since they are not needed anymore.
95+
96+
And everything ends with a final code cleanup where the shader code gets indented correctly (for the most part) and line terminators get normalized.
97+

.docs/guides/Code/BaseTemplate.txt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
ZTest[_ZTest]
2+
ZWrite[_ZWrite]
3+
Cull[_CullMode]
4+
5+
Pass
6+
{
7+
Tags
8+
{
9+
"LightMode" = "ForwardBase"
10+
}
11+
12+
CGPROGRAM
13+
#pragma target 3.0
14+
#pragma vertex Vertex
15+
#pragma fragment Fragment
16+
17+
#include "UnityStandardUtils.cginc"
18+
19+
struct VertexData
20+
{
21+
float4 vertex : POSITION;
22+
float2 uv : TEXCOORD0;
23+
float3 normal : NORMAL;
24+
};
25+
26+
struct FragmentData
27+
{
28+
float4 pos : SV_POSITION;
29+
float3 normal : NORMAL;
30+
float2 uv : TEXCOORD0;
31+
float3 worldPos : TEXCOORD1;
32+
};
33+
34+
FragmentData FragData;
35+
float4 FinalColor;
36+
37+
#K#DEFAULT_VARIABLES
38+
39+
#K#DEFAULT_CODE
40+
41+
FragmentData Vertex (VertexData v)
42+
{
43+
FragmentData i;
44+
UNITY_INITIALIZE_OUTPUT(FragmentData, i);
45+
46+
#K#VERTEX_FUNCTION
47+
48+
return i;
49+
}
50+
51+
float4 Fragment (FragmentData i) : SV_TARGET
52+
{
53+
FragData = i;
54+
FinalColor = float4(0,0,0,0);
55+
56+
#K#FRAGMENT_FUNCTION
57+
58+
return FinalColor;
59+
}
60+
61+
ENDCG
62+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
void ApplyColor()
2+
{
3+
FinalColor = _MyColor;
4+
}

.docs/guides/Code/ShaderCode.txt

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
Shader "Example/ExampleShader"
2+
{
3+
Properties
4+
{
5+
[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("Depth test", Float) = 4
6+
_ZWrite("Depth write", Float) = 0
7+
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull Mode", Float) = 2
8+
_MyColor("My Color", Color) = (1, 1, 1, 1)
9+
}
10+
SubShader
11+
{
12+
ZTest[_ZTest]
13+
ZWrite[_ZWrite]
14+
Cull[_CullMode]
15+
16+
Pass
17+
{
18+
Tags
19+
{
20+
"LightMode" = "ForwardBase"
21+
}
22+
23+
CGPROGRAM
24+
#pragma target 3.0
25+
#pragma vertex Vertex
26+
#pragma fragment Fragment
27+
28+
#include "UnityStandardUtils.cginc"
29+
30+
struct VertexData
31+
{
32+
float4 vertex : POSITION;
33+
float2 uv : TEXCOORD0;
34+
float3 normal : NORMAL;
35+
};
36+
37+
struct FragmentData
38+
{
39+
float4 pos : SV_POSITION;
40+
float3 normal : NORMAL;
41+
float2 uv : TEXCOORD0;
42+
float3 worldPos : TEXCOORD1;
43+
};
44+
45+
FragmentData FragData;
46+
float4 FinalColor;
47+
48+
float4 _MyColor;
49+
50+
void ApplyColor()
51+
{
52+
FinalColor = _MyColor;
53+
}
54+
55+
FragmentData Vertex (VertexData v)
56+
{
57+
FragmentData i;
58+
UNITY_INITIALIZE_OUTPUT(FragmentData, i);
59+
60+
i.pos = UnityObjectToClipPos(v.vertex);
61+
i.normal = UnityObjectToWorldNormal(v.normal);
62+
i.worldPos = mul(unity_ObjectToWorld, v.vertex);
63+
i.uv = v.uv;
64+
65+
return i;
66+
}
67+
68+
float4 Fragment (FragmentData i) : SV_TARGET
69+
{
70+
FragData = i;
71+
FinalColor = float4(0,0,0,0);
72+
73+
ApplyColor();
74+
75+
return FinalColor;
76+
}
77+
78+
ENDCG
79+
}
80+
81+
}
82+
}

0 commit comments

Comments
 (0)