Saturday, October 12, 2019

Adaptation of the Timbre & Crossfade from the Music Easel

Version 1

Original circuit by Don Buchla (used with his kind permission); adapted by Aaron Lanterman. This is based on the timbre circuit on Board 9 and the single-vactrol "waveshape" crossfade circuit on Borad 8 of the Music Easel. You should spend some time studying the original schematics. Warning: This board is designed to be highly flexible; it can be configured in many different ways. Please read the notes below carefully and decide what options you want before building.

Demo

Note this video is of Version 0, so pay no attention to any mention I make of errors on the PCB; those comments are not relevant to Revision 1.

Schematics & layout



Notes

  • I am convinced that the 50K sliders marked on the original schematics (and this version of the board) should actually be 10K linear. The 120K input and shaping resistors (R102, R103, R104, R105, R108, R109, R110, and R110) are off-board in the original Easel, but included on-board in this adaptation.
  • The original Easel has a 13.5 V supply, created using an op amp and a transistor buffer. If you have such a supply, you may hook it to the two +13.5 pins and omit R100, R101, R106, and R107. Otherwise, leave the +13.5 pins unconnected and use R100, R101, R106, and R107, which create "soft" +13.5 V supplies (via voltage dividers made by R100 with R101, and R106 with R107.) To counteract loading I found that lowering R100 and R106 from 10K to 1K is a good idea, so I marked these as 1K on the PCB. You may want to experiment with other values. 
  • The Q6 JFET is used as a variable resistor. It is specified as a 2N4341 in the original, but it appears to be out of production. I picked the J201 since it happened to come with the preinstalled Eagle libraries. I've tried a MPF102 here too, and didn't notice a difference. R21 is marked as 6.8K on the original Buchla schematic. I had to raise this value quite a lot in order to not get too much gain through the VCA with the offset knob at the lowest setting. I used 330K instead of 6.8K and marked R21 as such on the PCB. I recommend that as a starting point if you're using a J201 or MPF102 for Q6 (I tried both and didn't notice any difference). Your mileage may vary. Different values of R21 (even instances of the same JFET model type) may be appropriate for different choices of Q6.
  • I specified Q5 as a 2N3906 since I happen to have of them and it also came preinstalled in the Eagle libraries. In the original Music Easel schematic, it is specified as a 2N4248, which seems to be out of production. You might want to try other transistors here.
  • The circuit has been tested with RC4558s, which was deemed to be electrically similar to the original RC4136s used in the Easel. Other op amps will probably work (many will probably work better!), but they have not been tried.
  • D3 is a 1N457. I suspect a 1N4148s or a 1N914 will work, but I have not tested them.
  • D1 and D2 are not specified in the original schematic; I used 1N457s here, but my suspicions in the previous bullet point apply here too.

Connections

Front panel connections usually have a square and round pad together in a white box. The round pad is the signal, and the square pad provides a convenient ground.
  • TAI - Timbre Audio Input
  • TAO - Timbre Audio Output
  • TCVI - Timbre CV input; amount of influence is controlled by setting of TCV pot
  • WCVI - "Waveshape" crossfader CV input; amount of influence is controlled by setting of WCV pot
  • A1I - Alternate input 1; buffered and appears at A1B
  • A1B - Buffered version of A1I input; may be used connected to A1C, or not used at all, or connected to a switch
  • A1C - Corresponds to pin 10 of Board 8 of the original Easel schematics. It corresponds to what you get by turning the waveshape control counterclockwise. If you want to set this up like an original Easel, connect B9P4 or TOP directly A1C, so turning the waveshape control counterclockwise corresponds to the timbre circuit. If you want to always use the "waveshape" crossfader as a stand alone crossfader, you can directly hook A1B to A1C. If you'd like to switch between both options, hook A1C to the common terminal of a on-none-on SPDT switch, and hook TOP or B9P4 to one "on" terminal and A1B to another "on" terminal. (The issue of whether to use TOP or B9P4 is complex and depends on how you set the resistors OR119, OR120, OR1A, and OR47A; see below.)
  • TOP - Timbre Output Pin - connected to A1C, or to a switch, or not used (see options listed under the A1C description above). This is the timbre output after the gain provided by IC5B (if gain is used).
  • B9P4 - Corresponds to Pin 4 of Board 9 of the original Music Easel - connected to A1C, or to a switch, or not used (see options listed under the A1C description above). This is the timbre output before the gain provided by IC5B (assuming gain is used).
  • A2I - "Alternate" input 2; buffered and appears at B8P; used if creating a stand-alone module. This corresponds to what you get when turning the waveshape control clockwise.
  • B8P - Input to the Vactrol side of the "waveshape" crossfader. If you are using the A2I input, you won't need to use the B8P pad. If you are trying to build an complete Easel, B8P corresponds to pin 12 of IC 4 on the original Easel Board 8 schematic. This is the pulse, square, or triangle shape signal that you'd get by turning the waveshape control clockwise. If you hook a signal directly to B8P, you should omit IC7, R112, R113, R114, R115 (notice this also takes out the A1I, A1B functionality, but that's probably OK since you'll probably be directly hooked the timbre output to A1C anyway). Most users building stand-alone modules will probably not need to use B8P.
  • MXO - Mixed Output of the "waveshape" crossfader
  • CSW1, CSW2 - there's a capacitor that provides some filtering action on the timbre output. You can put a switch between CSW and CSW2 and experiment with switching this cap in and out. If you want it to act like an original Easel, just short CSW1 and CSW2.

Resistor options

  • If you are using an op amp with some build in short-circuit protection, like the specified RC4558s, then you can use the 220R resistors OR121 and OR48A, and use wires instead of 1K resistors for OR115 and OR122. If, on the other hand, you are using a different op amp capable of creating much bigger currents, I recommend using wires instead of 220R resistors for OR121 and OR48A, and installing actual 1K protection resistors in the OR115 and OR122 spots.
  • OK, here is where things get really complicated. OR1A and OR47A are specified as 15K and 75K; this is as things are in the original Easel. This gives the raw timbre signal at B8P4 and whatever it is mixed with at B8P a gain of 6. IC5B, OR119, OR120, OR121, and OR122 are not present in the original Easel; this is a copy of the circuitry around IC6B to give that gain of 6 at the TAO output. If you'd like your external signal input at A2I to be subject to the same gain, then you can use 15K and 75K in the OR1A and OR47A spots, respectively. However, you may prefer to take the timbre output to mixer from the TOP pin, so it already has the gain, in which case you can omit OR1A altogether, and use a wire for OR47A, which turns IC6B into a unity gain buffer; in this case, IC5B boosts the timbre output up to the level of typical signals, and will then be on an even footing with most external signals, and IC6B won't provide additional undesired gain. Think carefully about your particular desired gain structure.

Potentiometers

  • WOS - "Waveshape" croassfader Offset 
  • WCV - "Waveshape" crossfader CV; controls amount of influence of the WCVI input 
  • TOS - Timbre Offset 
  • TCV - Timbre CV; controls amount of influence of the TCVI input 

Disclaimer

  • These should be considered advanced projects, and should only be attempted by people with extensive knowledge and experience in electronics, particularly in terms of practical construction and debugging techniques. The boards are dense and the documentation is sparse. If you are just getting started with Synth DIY, I recommend starting with kits.
  • If you try to build one of these projects, you must assume that you will be on your own, and be confident enough to tackle the project under those circumstances. I am interested in learning about people's experiences in building the boards, and will try to answer questions over e-mail, but I don't have time to do any hand holding.
  • Any PCBs made available to the public are provided as-is, with no guarantees or warranties whatsoever. Similarly, no guarantees or warranties are made about the correctness or usefulness of the information on these webpages.
  • Any electronic project may present a risk of injury or death, particularly when dealing with mains voltages. It is important to follow appropriate safety practices. The author of this post, Aaron Lanterman, disclaims any liability for injury, death, or other damage caused in using the PCBs or any of the information contained on these webpages.

Adaptation of the Pulser & Inverter from the Music Easel

Revision 1

Original circuit by Don Buchla (used with his kind permission); adapted by Aaron Lanterman.
This is based on the pulser & inverter circuits on Board of the Music Easel. You should spend some time studying the original schematics.

Demo

Note this video is of Version 0, so pay no attention to any mention I make of errors on the PCB; those comments are not relevant to Revision 1.


Schematic & layout

Errors

  • There are some errors in the schematic and the silkscreen. Fortunately these only involve incorrect names and values; addressing these issues does not require any trace cutting or jumpering. First note that the "R11" and "R22" labels on the PCB are accidentally swapped; the parts themselves are in the correct place. The resistor closer to the 2N1711 transistor should be labled "R22" and the one closer to the MC14016s should be labeled "R11." Thanks to Dave Brown for catching this error.
  • If you look on my Eagle schematic around the 2N1711, you'll see R21, R22, and R28 are 6K8. This is a copy-and-paste error in the resistor values, since only one of them should be 6K8. R21 should be 6.8K, but R22 should be 2.2K (I think - it's hard to tell on the original Buchla schematic, it looks like it might be 22K?), and R28 should be 100R. Thanks to Dave Brown for catching these error; I never tried hooking an LED or light bulb up, so I never noticed this error before. Dave also noted that the 100R values for R28 may be specific to using an incandescent bulb.

Notes

  • I am convinced that the 50K sliders marked on the original schematics should actually be 10K linear. The 120K input and shaping resistors (R105, R106, R107, and R108) are off-board in the original Easel, but included on-board in this adaptation.
  • The original Easel has a 13.5 V supply, created using an op amp and a transistor. If you have such a supply, you may hook it to the +13.5 pin and omit R103 and R104. Otherwise, leave the +13.5 pins unconnected and use R103 and R104, which create a "soft" +13.5 V supply. I found it important to lower R103 to something like 3.3K to counteract loading, so I marked R103 as 3.3K on the PCB. You may want to experiment with other values.
  • The circuit has been tested with RC4558s, which was deemed to be electrically similar to the original RC4136s used in the Easel. Other op amps will probably work (many will probably work better!), but they have not been tried.
  • D3-D6 are 1N457s. I suspect a 1N4148s or a 1N914 will work, but I have not tested them.

Connections

Front panel connections usually have a square and round pad together in a white box. The round pad is the signal, and the square pad provides a convenient ground.PIC, PIO, FB - Pulse Input Common, Pulse Input One-Shot, and Feedback. You want to try to find a single-pole on-off-(on) switch, where the (on) indicates momentary operation. Hook PIC to the common switch terminal, hook PIO to the (on) terminal, and hook FB to the regular on terminal. This will let you do just one "pulse," or if you switch to the feedback mode quickly after doing one pulse, the pulser will drive itself and you will get repeated pulses. The middle position turns off the pulsing. If need be, you could just use a regular on-off-on switch here.
  • PCVA, PCVB - Pulser CV outputs A and B. A is active when AEN is set high; B is active when BEN is set high.
  • PPA, PPB - Pulser pulse outputs A and B. A is active when AEN is set high; B is active when BEN is set high.
  • Y1, Y2 - terminal of an electronic switch; connection made when BEN is set high (untested).
  • Z1, Z2 - terminals of an electronic switch; connection made when BEN is set high (untested).
  • ANOT, BNOT - logical "not" of AEN and BEN
  • AEN, BEN - A and B enables; see other connection instructions for details of what they enable. I plan to connect these to a switch that will let be switch between automatically-on (connect to +15 V) and connect to an external input. Most users will probably just want to tie AEN to +15 so the A outputs are always enabled. Some users may want to just ignore the B outputs entirely. Some might want to only use the "B" part of the circuit to control the Z1,Z2 and Y1,Y2 electronic switches, and ignore the pulser B outputs. Do whatever makes you happy.
  • INVI, INVO - inverter input and output; takes 0-10 V CV and outputs 10-0 V CV. The inverter is independent of the rest of the pulser, so you can invert whatever CV signals you want.
  • LED - on the Easel schematics, this is actually called "LAMP" and is shown going through a lamp-looking symbol to a +12 V supply. I haven't tried doing anything with this, since it's a low priority for me, but if someone can get something to light up I'd love to hear about it.

Potentiometers

  • LOS - Level (pulser rate) Offset
  • LCV - Level (pulser rate) CV; controls amount of influence of the LIN input
  • TRIM - Trims the pulser rate - set to personal taste

Disclaimer

  • These should be considered advanced projects, and should only be attempted by people with extensive knowledge and experience in electronics, particularly in terms of practical construction and debugging techniques. The boards are dense and the documentation is sparse. If you are just getting started with Synth DIY, I recommend starting with kits.
  • If you try to build one of these projects, you must assume that you will be on your own, and be confident enough to tackle the project under those circumstances. I am interested in learning about people's experiences in building the boards, and will try to answer questions over e-mail, but I don't have time to do any hand holding.
  • Any PCBs made available to the public are provided as-is, with no guarantees or warranties whatsoever. Similarly, no guarantees or warranties are made about the correctness or usefulness of the information on these webpages.
  • Any electronic project may present a risk of injury or death, particularly when dealing with mains voltages. It is important to follow appropriate safety practices. The author of this post, Aaron Lanterman, disclaims any liability for injury, death, or other damage caused in using the PCBs or any of the information contained on these webpages.


Adaptation of the Low Pass Gate from the Music Easel

Revision 1

Original circuit by Don Buchla (used with his kind permission); adapted by Aaron Lanterman
This is based on the lowpass gate circuit on Board 10 and Board 11 of the Music Easel, which contains two identical LPG circuits. You should spend some time studying the original schematics.

Schematic & Layouts







Notes

  • The holes and traces on the Revision 1 PCBs are the same as on Version 0. The only changes I made were to the silkscreen. I forgot to put a "Rev 1" marking on the board. You can tell it is a "Rev 1" board if it says "BEAD" in the spots near the power connector; the older version said "2R2." 
  • I have not been able to get the LED to light. I do not know why. (It appears that one of my beta testers has gotten an LED to light, though.)
  • I am convinced that the 50K sliders marked on the original schematics should actually be 10K linear. The 120K input and shaping resistors (R105, R106, R107, and R108) are off-board in the original Easel, but included on-board in this adaptation.
  • The original Easel has a 13.5 V supply, created using an op amp and a transistor. If you have such a supply, you may hook it to the +13.5 pin and omit R103 and R104. Otherwise, leave the +13.5 pin unconnected and use R103 and R104, which create a "soft" +13.5 V supply. In testing, this was found to droop to between 9 V and 11 V depending on pot settings, resulting in me being unable to open up the filter all the way using just the LOS pot. I lowered R103 to 3.3K, and found that this helped counteract the droop and I got a reasonable full-range control, so I marked R103 as 3.3K on the board. You may want to experiment with other values for R103. An alternative would be to keep the R103/R104 ratio the same but lower the overall values, such as reducing R103 to 1K and R104 to 9.1K. However, I have not tried this.
  • The area around the vactrols is tight; be sure to install R42 and R41 before installing the vactrols. Also, the 910 pf silver mica caps are pretty big; to get them installed I had to leave them kind of floating above most of the other parts.
  • Q2, the buffer JFET, is a 2N4340 in the original. I picked the J201 since it happened to come with the preinstalled Eagle libraries. I used an actual J201 in my build and it worked fine. Any JFET you have previously successfully used as an audio buffer should work fine here.
  • The need for R100, the 68K input resistor, was gleaned by studying other parts of the original Easel schematics.
  • The circuit has been tested with RC4558s. Other op amps will probably work (many will probably work better!), but they have not been tried.
  • The regular diode in the original is a 1N457. I suspect a 1N4148s or a 1N914 will work, but I have not tested them.
  • Dr. Mabuse has run into a problem with excessive current draw cooking parts - read about the problem and his solution here. I personally haven't been able to reproduce whatever the problem is. I would be very interested to see if other people do (or don't) run into this problem.
  • Dr. Mabuse reports that a 0.001 uf cap (i.e. 1 nf) works fine in place of the 910 pf cap around the LED-driving 2N1711 transistor; this lets you save 910 pf micas for more critical audio path applications.
  • Dr. Mabuse writes: "Another sub that I decided against but still yielded useful and interesting results was swapping a single VTL5C3/2 for two VTL5C3s. It works both as a filter and as a VCA but the response curve is noticeably different (not as even and smooth) and the VCA mode didn't attenuate quite as much. In a pinch it think it could be used though."

Connections

Front panel connections usually have a square and round pad together in a white box. The round pad is the signal, and the square pad provides a convenient ground.

  • LIN - Level CV Input; amount of influnce controlled by setting of LCV pot
  • AIN - Audio input
  • AO - Audio output
  • LED - Hooks to the cathode (straight line part of symbol, shorter leg of actual device) of an LED; the anode (triangle part of symbol, longer leg of actual device) of the LED is hooked to +5 V.
  • SWV, SWC, and SWL - Connections for the mode switch. Use a SPDT on-off-on switch. Connect SWC to the common connection, SWV to the lower connection, and SWL to the upper connection. Switching to connect SWC to SWL puts the filter in lowpass mode; switching to connect SWC to SWV puts it in VCA mode, and switching it to the "off" position puts it in "combo" mode.
  • CIN - Control input. CV input for mode control; amount of influence is controlled by the CCV pot. If +13.5 V is input here, then the resistance of CCV corresponds to the resistor setting on an Easel programming card, but you can put in all sorts of varying voltages here. I have not tried to puzzle out exactly what effect this has, i.e. how many volts at a given pot setting is required to change modes, etc., but I have made it switch modes. This input piles "on top of" the switch setting, so its influence will change with switch settings.
  • B10P1 - Analogous to Pin 1 on Board 10; maybe useful if you are using this to replace an original Easel board. Most users will not need this.
  • B10P9 - Analogous to Pin 9 on Board 10; maybe useful if you are using this to replace an original Easel board. Most users will not need this.

Potentiometers

  • LOS - Level Offset
  • LCV - Level CV; controls amount of influence of the LIN input
  • CCV - Control CV; controls amount of influence of CCV. I specified 300K here, but I largely pulled that number out of a hat. I would suggest a linear pot, but I'm really not sure if a log or linear pot would be best. If +13.5 V is put into CIN, then CCV corresponds to the resistor setting on an Easel programming card, but you can put in all sorts of changing voltages for CIN.

Disclaimer

  • These should be considered advanced projects, and should only be attempted by people with extensive knowledge and experience in electronics, particularly in terms of practical construction and debugging techniques. The boards are dense and the documentation is sparse. If you are just getting started with Synth DIY, I recommend starting with kits.
  • If you try to build one of these projects, you must assume that you will be on your own, and be confident enough to tackle the project under those circumstances. I am interested in learning about people's experiences in building the boards, and will try to answer questions over e-mail, but I don't have time to do any hand holding.
  • Any PCBs made available to the public are provided as-is, with no guarantees or warranties whatsoever. Similarly, no guarantees or warranties are made about the correctness or usefulness of the information on these webpages.
  • Any electronic project may present a risk of injury or death, particularly when dealing with mains voltages. It is important to follow appropriate safety practices. The author of this post, Aaron Lanterman, disclaims any liability for injury, death, or other damage caused in using the PCBs or any of the information contained on these webpages.

Tuesday, August 27, 2019

Adaptation of the Balanced Modulator from the Music Easel

Revision 2

Original circuit by Don Buchla (used with his kind permission); adapted by Aaron Lanterman. This is based on the balanced modulator circuit on Board 5 of the Music Easel. You should spend some time studying the original schematics. Warning: This board is designed to be highly flexible; it can be configured in many different ways. Please read the notes below carefully and decide what options you want before building.

Demos

Note this is a demo of Version 0. Revision 2 doesn't require all of those horrible kludges seen in the video.

Schematic & layouts

Notes

  • I am convinced that the 50K sliders marked on the original schematics should actually be 10K linear. The 120K input and shaping resistors (R117, R118, R119, R120) are off-board in the original Easel, but included on-board in this adaptation.
  • The original Easel has a 13.5 V supply, created using an op amp and a transistor. If you have such a supply, you may hook it to the +13.5 pin and omit R115 and R116. Otherwise, leave the +13.5 pins unconnected and use R115 and R116, which create a "soft" +13.5 V supply. You may want to experiment with changing these settings a bit, for instance lowering R115 and R116, to counteract loading. I found that lowering R115 from 10K to 3.3K is seemed to work well.
  • If you want to use the FO fuzz output, you should add a 0.1 microfarad DC blocking cap, like the FOA and FOB outputs have. It should be pretty easy to add this somewhere on the way between the board and the FO jack.
  • The circuit has been tested with RC4558s. Other op amps will probably work (many will probably work better!), but they have not been tried.

Connections

  • Front panel connections usually have a square and round pad together in a white box. The round pad is the signal, and the square pad provides a convenient ground.
  • MI - Modulation Input
  • SI - Signal Input
  • EW, ED - External Wet, External Dry - the balanced modulator has a crossfader that lets the user fade between the "dry" signal input and the "wet" full ring mod signal. These inputs let the user put in alternative "wet" and "dry" signals, so the crossfader may be used as a stand-alone submodule separate from the ring mod.
  • MIXO - Mixed Output of the crossfader - this is the actual output of the "real Easel" balanced modulator
  • RMO - Ring Modulator Output - this is the pure ring mod signal.
  • FO, FOA, and FOB - fuzz outputs - various highly distorted versions of the modulation input signal. These taps were suggested by Grant Richter (who had many helpful suggestions on this particular project.)
  • CVIN - crossfader CV input; amount of influence is controlled by setting of CV pot
  • WSC, WSE, WSW - switch to select what goes in the "wet" side of the crossfader, if you want this facility. Hook WSC to the common of an on-none-on single pole switch, and connect WSE ("external wet" select) and WSW (full ring mod select) to the poles. If you don't want this facility, and want it to act like an original Easel, i.e. the crossfader is dedicated to the ring mod, then simply hook WSW to WSC.
  • DSC, DSE, DSW - switch to select what goes in the "dry" side of the crossfader, if you want this facility. Hook DSC to the common of an on-(none)-on single pole switch, and connect DSE ("external dry" select) and DSW (signal input select) to the poles. If you don't want this facility, and want it to act like an original Easel, i.e. the crossfader is dedicated to the ring mod, then simply hook DSD to DSC.
  • Note that if you take the "act like an original Easel" approach in the last two bullet points, you may omit IC5 and R104, R105, R106, R107, R108, and R109. Also note that when external inputs are being used, the terms "wet" and "dry" just denote different inputs - those external inputs can be whatever you want.)
  • B5IC2P8, BIC2P2, BIC5P10 - these correspond to various points on the original Easel schematic, if desired. The ICXPY notation refers to IC X (on the original Easel schematic), pin Y (original Easel IC pins). They might be useful to someone trying to clone a full Easel, but most users will not need to use these.
  • VI - "variant input" - OK, this may get confusing (uhm, even more confusing, I mean.) On the Easel, the control circuitry for the vactrols in the crossfader for the ringmod drives an additional vactrol, which sort of forms a VCA for the FM input for the principle oscillator. Here, I've set it up as a stand-alone VCA that you can do whatever you want with.
  • VO - "variant output" - output of the VCA described above
  • VSC, VCE, VSM - switch to select what to send to the "variant" VCA. Connect VSC to the common of an on-none-on single-pole switch, and connect VCE to VSM to the poles. When switched to VCE, it will send the VI signal to the VCA. When switched to VSM, it will send the modulation input (MI) to the VCA (this is the "real Easel") behavior. Of course, you can omit the switch and just tie VSC directly to VSM or VCE, if you want to do such a thing.

Resistor options

  • If you are using an op amp with some build in short-circuit protection, like the specified RC4558s, then you can use the 220R resistors OR122, OR123, and OR124, and use wires instead of 1K resistors for OR112, OR113, OR114. If, on the other hand, you are using a different op amp capable of creating much bigger currents, I recommend using wires instead of 220R resistors for OR122, OR123, and OR124, and installing actual 1K protection resistors in the OR112, OR113, OR114, spots.

Potentiometers

  • offset - crossfader offset (original Easel schematics say 50K, but I believe that is an error; I recommend 10K linear)
  • CV - crossfader CV; controls amount of influence of the WCVI input (original Easel schematics say 50K, but I believe that is an error; I recommend 10K linear)
  • SYMTR - I found the easiest way to trim this is to modulate an audio signal with an LFO, and trim it until you get similar amplitude beats.

Disclaimer

  • These should be considered advanced projects, and should only be attempted by people with extensive knowledge and experience in electronics, particularly in terms of practical construction and debugging techniques. The boards are dense and the documentation is sparse. If you are just getting started with Synth DIY, I recommend starting with kits.
  • If you try to build one of these projects, you must assume that you will be on your own, and be confident enough to tackle the project under those circumstances. I am interested in learning about people's experiences in building the boards, and will try to answer questions over e-mail, but I don't have time to do any hand holding.
  • Any PCBs made available to the public are provided as-is, with no guarantees or warranties whatsoever. Similarly, no guarantees or warranties are made about the correctness or usefulness of the information on these webpages.
  • Any electronic project may present a risk of injury or death, particularly when dealing with mains voltages. It is important to follow appropriate safety practices. The author of this post, Aaron Lanterman, disclaims any liability for injury, death, or other damage caused in using the PCBs or any of the information contained on these webpages.

Adaptation of the Envelope Generator from the Music Easel

Revision 1

Original circuit by Don Buchla (used with his kind permission); adapted by Aaron Lanterman. This is based on the envelope generator circuit of Board 3 of the Music Easel. You should spend some time studying the original schematics.

Demo

Schematic & layouts

Notes

  • The holes and traces on the Revision 1 PCBs are the same as on Version 0. The only changes I made were to the silkscreen.
  • I am convinced that the 50K sliders marked on the original schematics should actually be 10K linear. The 120K input and shaping resistors (R117 through R128) are off-board in the original Easel, but included on-board in this adaptation.
  • The circuit has been tested with RC4558s, which was deemed to be electrically similar to the original RC4136s used in the Easel. Other op amps will probably work (many will probably work better!), but they have not been tried.
  • Buchla went to the effort to specify 1N457 in one instance and 1N457A in another instance, so I'd be nervious about changing what dioes are used. I'd be curious if to know if other diodes, such as 1N4148s or 1N914s, will work.
  • Bjorn wrote: "I am not sure whether a solution to get the transient function of the envelope generator with a regular gate signal has been proposed yet. Anyways, I have been able to get this functionality by replacing R101 with a 10nf cap."

Connections

  • Front panel connections usually have a square and round pad together in a white box. The round pad is the signal, and the square pad provides a convenient ground.
  • ATIN, DURIN, DECIN - attack, duration, and decay CV inputs; influence is controlled by ATCV, DURCV, and DECCV settings, respectively
  • PIN - pulse input
  • B3P6 - corresponds to original Easel Board 3, Pin 6
  • TRAN, SUS - connect TRANS and SUS to the extremes of a single-pole on-none-on switch, and connect B3P6 to the common terminal of this switch. This lets the user switch been "transient" and "sustained" modes. (Note I only tested the transient functionality.)
  • POUT - pulse output
  • ENVOUT - envelope output
  • LED - on the Easel schematics, this is actually called "LAMP" and is shown going through a lamp-looking symbol to a +12 V supply. I haven't tried doing anything with this, since it's a low priority for me, but if someone can get something to light up I'd love to hear about it.
  • +13.5 V - a supply created using a buffer op amp (most users will not need this)

Potentiometers

  • ATOS, DUROS, DECOS - attack, duration, and decay offsets
  • ATCV, DURCV, DECCV - attack, duration, and decay CV controls; control influence of ATIN, DURIN, and DECIN inputs, respectively

Disclaimer

  • These should be considered advanced projects, and should only be attempted by people with extensive knowledge and experience in electronics, particularly in terms of practical construction and debugging techniques. The boards are dense and the documentation is sparse. If you are just getting started with Synth DIY, I recommend starting with kits.
  • If you try to build one of these projects, you must assume that you will be on your own, and be confident enough to tackle the project under those circumstances. I am interested in learning about people's experiences in building the boards, and will try to answer questions over e-mail, but I don't have time to do any hand holding.
  • Any PCBs made available to the public are provided as-is, with no guarantees or warranties whatsoever. Similarly, no guarantees or warranties are made about the correctness or usefulness of the information on these webpages.
  • Any electronic project may present a risk of injury or death, particularly when dealing with mains voltages. It is important to follow appropriate safety practices. The author of this post, Aaron Lanterman, disclaims any liability for injury, death, or other damage caused in using the PCBs or any of the information contained on these webpages.

Saturday, August 24, 2019

Demo of a Korg Delta VCF adaptation my students made

While sorting through e-mail, I came across this link to a video that some of my Analog Circuits for Music Synthesis students made demoing their final project.


Monday, July 22, 2019

Customizing the Lit Shader in Unity's Lightweight Render Pipeline

Unity used to have a "Surface Shader" system by which you could write a function that would specify certain material properties based on various textures, slider settings, etc., and then Unity would compile the numerous shaders needed to support various functions like shadows, deferred rendering, etc. without you having to manually write each one. It also let you write custom lighting functions for that Surface Shader framework.

Surface Shaders were part of the original Unity built-in render pipeline. But, the new Scriptable Render Pipelines, namely the High Definition RP and the Lightweight RP, don't have Surface Shaders. They do have a graphical node-based shader programming facility called Shader Graph. Besides the usual problems with graphical programming paradigms (namely, a few lines of textual code can into verbose visual spaghetti code, with confusing lines connecting many blocks spread out over multiple screens), Shader Graph restricts you to the particular lighting models Unity provides in the form of Master Nodes. Some have tried to work around this by doing lighting calculations in the blocks and feeding the final result into the "emission" input of a master node; however, getting lighting info into the Shader Graph is tough. Another options is to save the HLSL generated by the Shader Graph compiler and modify that; unfortunately, the compiler assigns the links between nodes arbitrary variable name that look like license plate numbers, making the generated HLSL difficult to follow.

These problems with Shader Graph lead to the sad conclusion that if you want to create your own lighting models, you need to copy and modify one of Unity's hand-crafted ubershaders, even if that means duplicating a lot of effort in different shader passes. 

If you want to implement a custom lighting model, you'll be spending a lot of time trying to understand Lighting.hlsl. It contains LightweightFragmentPBR (used by Lit shader) and LightweightFragmentBlinnPhong (used by Simple Lit shader). Lighting.hlsl imports from Common.hlsl, EntityLighting.hlsl, ImageBasedLighting.hlsl, Core.hlsl, and Shadows.hlsl.

We'll also talk explore dynamic vertex and texture coordinate modifications. These are easily implemented with Shader Graph, but we might as well address them while we're here.

LWRP Lit Shader Passes

Lit.shader consists of four passes: ForwardLit, ShadowCaster, DepthOnly, and Meta; they import LitForwardPass.hlsl, ShadowCasterPass.hlsl, DepthOnlyPass.hlsl, and LitMetaPass.hlsl, respectively. All four passes import from LitInput.hlsl.

Forward Lit Pass: The forward lit pass vertex shader is LitPassVertex and its fragment shader is LitPassFragment. These are defined in LitForwardPass.html, which imports from Lighting.hlsl. LitPassFragment calls InitializeStandardLitSurfaceData (defined in LitInput.hlsl), InitializeInputData (defined in LitForwardPass.hlsl), MixFog (defined in Core.hlsl), and LightweightFragmentPBR (defined in Lighting.hlsl).

Shadow Caster Pass: The shadow caster pass vertex shader is ShadowPassVertex and its fragment shader is ShadowPassFragment. These are defined in ShadowCasterPass.hlsl, which imports from Core.hlsl and Shadows.hlsl. ShadowPassFragment calls SampleAlbedoAlpha and Alpha (defined in SurfaceInput.hlsl). The Alpha function contains a clip command; it looks like ShadowCasterPass uses BaseMap only to the extent that it provides alpha clipping. ShadowPassFragment returns 0, so it doesn't really do anything else interesting.

Depth Only Pass: The depth only pass vertex shader is DepthOnlyVertex, and its fragment shader is DepthOnlyFragment. These are defined in DepthOnlyPass.hlsl, which imports Core.hlsl. DepthOnlyFragment calls SampleAlbedoAlpha and Alpha, which are defined in SurfaceInput.hlsl. The Alpha contains a clip command; it looks like DepthOnlyPass uses BaseMap only to the extent that it provides alpha clipping. (This was the case with ShadowCasterPass). DepthOnlyFragment returns 0, so it doesn't really do anything else interesting.

Meta Pass: The meta pass vertex shader is LightweightVertexMeta, and its fragment shader is LightweightFragmentMeta. These are defined in LitMetaPass.hlsl, which imports MetaInput.hlsl. LightweightVertexMeta calls MetaVertexPosition, which is defined in MetaInput.hlsl. LightweightFragmentMeta calls MetaFragment (defined in MetaInput.hlsl), InitializeStandardLitSurfaceData (defined in LitInput.hlsl), and InitializeBRDFData (defined in Lighting.hlsl).

If unity_MetaFragmentControl.x is true, it returns a (possibly tweaked) albedo value. If unity_MetaFragmentControl.y is true, it returns an emission value. This is confusing because the comments at the top say "y = return normal" and I don't see where it's
computing or storing a normal.


Passes for Other LWRP Shaders

SimpleLit.shader consists the same four passes: ForwardLit, ShadowCaster, DepthOnly, and Meta; they import SimpleLitForwardPass.hlsl, ShadowCasterPass.hlsl, DepthOnlyPass.hlsl, and SimpleLitMetaPass.hlsl, respectively. Note that the ShadowCaster and DepthOnly passes import the same hlsl files as the Lit shader, whereas the ForwardLit and Meta passes import hlsl files specialized for the SimpleLit shader. All four passes import from SimpleLitInput.hlsl instead of LitInput.hlsl. The SimpleLit specialized versions of files lack the additional fields and functions needed for a full PBR model. It invokes LightweightFragmentBlinnPhong (in Lighting.hlsl) instead of LightweightFragmentPBR, which is used by Lit.shader.

BakedLit.shader only has three passes: BakedLit, DepthOnly, and Meta.

Unlit.shader only has three passes: Unlit, DepthOnly, and Meta.

I won't explore BakedLit or Unlit shaders further here. I also won't explore the hey have even less functionality; I will not explore the ParticlesLit, ParticlesSimpleLit, ParticlesUnlit, or Terrain shaders.


Dynamic Vertex Modification

Forward Lit Pass: You could insert your vertex, normal, and tangent modification code
just before these lines in LitVertexPass (in LitForwardPass.hlsl), modifying the
positionOS, normalOS, and tangentOS fields in the "input" Attributes structure as desired:

VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);

VertexPositionInputs and VertexNormalInputs are defined in Core.hlsl as:

struct VertexPositionInputs {
    float3 positionWS; // World space position
    float3 positionVS; // View space position
    float4 positionCS; // Homogeneous clip space position
    float4 positionNDC;// Homogeneous normalized device coordinates
};

struct VertexNormalInputs {
    real3 tangentWS;
    real3 bitangentWS;
    float3 normalWS;
};

The associated functions are defined as (note VertexNormalInputs is overloaded):

VertexPositionInputs GetVertexPositionInputs(float3 positionOS) {
    VertexPositionInputs input;
    input.positionWS = TransformObjectToWorld(positionOS);
    input.positionVS = TransformWorldToView(input.positionWS);
    input.positionCS = TransformWorldToHClip(input.positionWS);
   
    float4 ndc = input.positionCS * 0.5f;
    input.positionNDC.xy = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w;
    input.positionNDC.zw = input.positionCS.zw;
       
    return input;
}

VertexNormalInputs GetVertexNormalInputs(float3 normalOS) {
    VertexNormalInputs tbn;
    tbn.tangentWS = real3(1.0, 0.0, 0.0);
    tbn.bitangentWS = real3(0.0, 1.0, 0.0);
    tbn.normalWS = TransformObjectToWorldNormal(normalOS);
    return tbn;
}

VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS) {
    VertexNormalInputs tbn;

    // mikkts space compliant. only normalize when extracting normal at frag.
    real sign = tangentOS.w * GetOddNegativeScale();
    tbn.normalWS = TransformObjectToWorldNormal(normalOS);
    tbn.tangentWS = TransformObjectToWorldDir(tangentOS.xyz);
    tbn.bitangentWS = cross(tbn.normalWS, tbn.tangentWS) * sign;
    return tbn;
}

Shadow Caster Pass: There's two places you could insert your vertex and normal modification code. One possible hack would be to put your code at top of GetShadowPostionHClip (in ShadowCasterPass.hlsl), before the lines:

float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);

A more elegant and clearer approach would be to modify the positionOS ad normalOS fields in the "input" Attributes structure just before this line in ShadowPassVertex:

output.positionCS = GetShadowPositionHClip(input);

Depth Only Pass: You could insert your vertex modification code in DepthOnlyVertex (in DepthOnlyPass.hlsl) just before the line

output.positionCS = TransformObjectToHClip(input.position.xyz);

For clarity, I'd probably write something like:

float3 modifiedPosition = SomeModificationOf(input.position.xyz);
output.positionCS = TransformObjectToHClip(modifiedPosition);

The stock depth only pass doesn't contain incorporate normals, which are often useful in creating interesting vertex deformations. Perhaps you could just add a "float3 normal : NORMAL;" line to the Attributes structure. I haven't tried this, though.

Meta Pass: You shouldn't need to touch the meta pass, since by definition meta passes are only invoked by the lightmapper, and hence are only useful on static geometry.


Dynamic Texture Coordinate Modification

Forward Lit Pass: The best place to modify texture lookups will depend on your particular goals. LitPassFragment has the line:

InitializeStandardLitSurfaceData(input.uv, surfaceData);

You could potentially modify the input.uv argument there, and that would more or less modify every texture lookup accordingly. For more fine-grained control, you could
modify InitializeStandardLitSurfaceData (in LitInput.hlsl), and modify the uv argument
inputs, as desired, in lines like:

half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
outSurfaceData.occlusion = SampleOcclusion(uv);
outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));

SampleOcclusion and SampleMetallicSpecGloss are defined in LitInput.hlsl. SampleAlbedoAlpha, SampleNormal, and SampleEmission are defined in SurfaceInput.hlsl.

Shadow Caster Pass and Depth Only Pass: ShadowPassFragment (in ShadowCasterPass.html) and DepthOnlyFragment (in DepthOnlyPass.html) both the line:

Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);

You could modify the input.uv argument as desired. You would only need to do this if
you are using a clip test with the alpha channel and want to animate it.

Meta Pass: You shouldn't need to touch the meta pass, since by definition meta passes are only invoked by the lightmapper, and hence are only useful on static geometry.


Getting Ready to Customize the Lighting Model

Basically, you'll need to make your own variation of LightweightFragmentPBR, and variations of the macros and functions is calls, if needed.

Make a new "Shader" folder inside your main Assets folder if you haven't done so already.

Via dragging and dropping, copy Packages/Lightweight RP/Shaders/Lit.shader into your new "Shader" folder. Rename your copy of Lit.shader and change the Shader "Lightweight Render Pipeline/Lit" line at the top of the file as desired; I changed it to MyLit.hlsl and Shader "My Custom/My Lit" -- but you can use whatever names you want.

Sadly, the copying will break a bunch of filename include references. Search on #include in MyLit.hlsl, and change the four instances of the lines #include "LitInput.hlsl" to

#include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitInput.hlsl"

Change the instance of #include "DepthOnlyPass.hlsl" to

#include "Packages/com.unity.render-pipelines.lightweight/Shaders/DepthOnlyPass.hlsl"

Change the instance of #include "ShadowCasterPass.hlsl" to

#include "Packages/com.unity.render-pipelines.lightweight/Shaders/ShadowCasterPass.hlsl"

Change the instance of #include "LitMetaPass.hlsl" to

#include "Packages/com.unity.render-pipelines.lightweight/Shaders/LitMetaPass.hlsl"
       
Via dragging and dropping, copy Packages/Lightweight RP/Shaders/LitForwardPass.hlsl into your new "Shader" folder. Rename your copy of LitForwardPass.hlsl. I changed it to MyLitForwardPass.hlsl -- but you can use whatever name you want.

In MyLit.shader, change #include "LitForwardPass.hlsl" to #include "MyLitForwardPass.hlsl"

The above assumes that you're not doing any dynamic vertex modification. If you are, you'll need to create a custom MyDepthOnlyPass.hlsl and MyShadowCasterPass.hlsl and change the various #includes appropriately. The above also assumes you're not doing any dynamic texture modifications that includes alpha clip tests; these will also necessitate custom MyDepthOnlyPass.hlsl and MyShadowCasterPass.hlsl files.

At this point, you should be able to take a material with the original LWRP Lit shader, switch it to your custom shader, and it should maintain all of its various textures and parameter settings and perform identically.

There's a couple of minor tweaks you may want to make before doing some major shader hacking:

1) To keep the vertex and fragment shader naming conventions consistent, in MyLit.shader, I'd recommend changing

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

to something like

#pragma vertex MyShadowPassVertex
#pragma fragment MyShadowPassFragment

2) If you want to modify the custom material inspector, make a new "Editor" folder inside your main Assets folder if you haven't done so already. Copy (via drag and drop) Packages/Lightweight RP/Editor/ShaderGUI/Shaders/LitShader.cs and Packages/Lightweight RP/Editor/ShaderGUI/ShadingModels/LitGUI.cs. Change the names of the files to MyLitShader.cs and MyLitGUI.cs (or whatever else you'd like).

In MyLitShader.cs and MyLitGUI.cs, change all the instances of LitGUI to MyLitGUI. In MyLitShader.cs, and LitShader in the class declaration to MyLitShader.

At the bottom of MyLit.shader, change the CustomEditor line to read

CustomEditor "UnityEditor.Rendering.LWRP.ShaderGUI.MyLitShader"

Coming up with a custom material inspector from scratch would be extremely painful, but it's not too taxing to make minor tweaks to an already working custom inspector.


Actually Customizing the Lighting Model

I recommend leaving the original Lighting.hlsl file, as well as the files it includes, intact. You can copy structures, functions, macros, etc. from those files, paste them into your MyLitForwardPass.hlsl, change the names, and then make your modifications; it seems easiest to have everything in one spot. If you wind up developing a lot of different shaders that share various bits of custom lighting code, you might want to break those out into a separate MyLighting.hlsl include file, or something like that.

Digging into MyLitPassFragment in MyLitForwardPass.hlsl, you'll want to drill down into InitializeStandardLitSurfaceData (defined in LitInput.hlsl), InitializeInputData (defined in MyLitForwardPass.hlsl), MixFog (defined in Core.hlsl), and LightweightFragmentPBR (defined in Lighting.hlsl).

MixFog (defined in Core.hlsl) is a one-line function that calls MixFogColor. It interpolates between the color computed by the shader and unity_FogColor.rgb according to fogFactor and some defines (FOG_LINEAR, FOG_EXP, FOD_EXP2). If you want to inject more complicated fog models, this would be the place to do it; otherwise, you probably don't need to change this. (That all said: I can't figure out where to turn on fog in the LWRP anyway! I will save that for another day).

InitializeStandardLitSurfaceData reads from various textures to populate a SurfaceData structure. If you want dynamic texture coordinate modifications that are particular to each texture, this would be a good place to do it. If you want to apply the same texture coordinate modification to everything, you might want to do it in the call to InitializeStandardLitSurfaceData (in MyLitPassFragment) instead, and leave the InitializeStandardLitSurfaceData function itself alone.

SurfaceData is defined in SurfaceInput.hlsl as:

struct SurfaceData {
    half3 albedo;
    half3 specular;
    half  metallic;
    half  smoothness;
    half3 normalTS;
    half3 emission;
    half  occlusion;
    half  alpha;
};

This definition is preceded has the comment "Must match Lightweight ShaderGraph master node." This is interesting, because it represents a connection point between the LWRP ShaderGraph framework and the primary hand-optimized, heavy-on-the-multicompile LWRP Lit shader. SurfaceData is quite reminiscent of the SurfaceOutput, SurfaceOutputStandard, and SurfaceOutputStandardSpecular structures of the Unity's old text-based Surface Shader system, which would make InitializeStandardLitSurfaceData analogous to the Surface Shader's surface functions.

InitializeInputData populates an InputData structure (defined in Input.hlsl) containing various quantities needed for lighting calculations:

struct InputData {
    float3  positionWS;
    half3   normalWS;
    half3   viewDirectionWS;
    float4  shadowCoord;
    half    fogCoord;
    half3   vertexLighting;
    half3   bakedGI;
};


LightweightFragmentPBR

Now we're at the main guts of MyLitPassFragment, namely the call to LightweightFragmentPBR, which is found in lighting.hlsl. It first calls InitializeBRDFData (in Lighting.hlsl), which pre-computes a bunch of values and stores them in a BRDFData struct:

struct BRDFData {
    half3 diffuse;
    half3 specular;
    half perceptualRoughness;
    half roughness;
    half roughness2;
    half grazingTerm;

    // We save some light invariant BRDF terms so we don't have to recompute
    // them in the light loop. Take a look at DirectBRDF function for detailed explaination.
    half normalizationTerm;     // roughness * 4.0 + 2.0
    half roughness2MinusOne;    // roughness² - 1.0
};

InitializeBRDFData sets the grazingTerm to saturate(smoothness + reflectivity); I confess I have no idea where that comes from.

The lighting calculation goes through four steps:

1) It adds the effect of global illumination (via a call to GlobalIllumination, found in Lighting.hlsl) and the effect of the "main light" (via a call to LightingPhysicallyBased, also found in Lighting.hlsl). In the process, it calls MixRealTimeAndBakedGI, which calls SubtractDirectMainLightFromLightmap (both defined in Lighting.hlsl); this implements an interesting but really complicated hack that I'm not going to explore further here.

2) It loops through the remaining lights, calling LightingPhysicallyBased on each one.

3) Unity's LWRP and built-in pipeline have a fairly complicated light hierarchy, in which up to four "unimportant lights" can be calculated with per-vertex lighting. It adds the results of those unimportant lights via a vertexLighting field in the inputData structure. This is assigned by the line

inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;

in InitializeInputData (found in MyLitForwardPass.hlsl).

fogFactorAndVertexLight, which is part of the Varying structure that connects the vertex shader and the fragment shader through interpolation, is assigned in MyLitPassVertex, which calls VertexLighting, which in turn calls LightingLambert (both are defined in Lighting.hlsl).

4) It adds the emissive color.


Global Illumination

The GlobalIlumination function is short enough that I'll include the code here:

half3 GlobalIllumination(BRDFData brdfData, half3 bakedGI, half occlusion, half3 normalWS, half3 viewDirectionWS) {
    half3 reflectVector = reflect(-viewDirectionWS, normalWS);
    half fresnelTerm = Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));

    half3 indirectDiffuse = bakedGI * occlusion;
    half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion);

    return EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
}

Here, the fresnelTerm uses dot(normalWS, viewDirectionWS), which is what is customary for indirect light, in contrast with direct light, which dot products the half vector with the view vector, or equivalently, the half vector with the light vector.

GlobalIllumination is called from this line in LightweightFragmentPBR (in Lighting.hlsl):

half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS);

In InitializeInputData (in MyLitForwardPass.hlsl), we find the line:

inputData.bakedGI = SAMPLE_GI(input.lightmapUV, input.vertexSH, inputData.normalWS);

SAMPLE_GI is a macro that invokes SampleLightmap on objects with lightmaps and SampleSHPixel (for spherical harmonic lighting) on objects without lightmaps.

GlossyEnvironmentReflection does a lookup into a cube map, choosing a mip level appropriate with to the roughness.

EnvironmentBRDF is short enough that I will include it here:

half3 EnvironmentBRDF(BRDFData brdfData, half3 indirectDiffuse, half3 indirectSpecular, half fresnelTerm) {
    half3 c = indirectDiffuse * brdfData.diffuse;
    float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0);
    c += surfaceReduction * indirectSpecular * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm);
    return c;
}

I have no idea where the surfaceReduction term comes from.

Direct Illumination

LightingPhysicallyBased (in Lighting.hlsl) has two overloaded variations:

half3 LightingPhysicallyBased(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS) {
    half NdotL = saturate(dot(normalWS, lightDirectionWS));
    half3 radiance = lightColor * (lightAttenuation * NdotL);
    return DirectBDRF(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * radiance;
}

half3 LightingPhysicallyBased(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS) {
    return LightingPhysicallyBased(brdfData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS);
}

It looks like most of the work is done in the curiously misspelled DirectBDRF (it should be DirectBRDF), which implements an approximate Cook-Torrance model.


Where to Start Putting In a Custom Lighting Model

LightingPhysicallyBased handles direct lights; that's the easiest place to put in modified diffuse models (Oren-Nayer, Minnaert, Disney Diffuse, etc.), modified specular models, or completely weird lighting models. A good exercise would be to replace the optimized-for-mobile LWRP Lit model and replace it with as much as you can of the HDRP Lit model.

Weird lighting models are less likely to make sense with any of the global illumination facilities like lightmaps, reflection probes, and light probes. But it's possible that you could partially incorporate modified diffuse and modified specular models. Unity's lightmappers are largely black boxes, so it would be difficult to calculate lightmaps under anything except for the usual Lambertian light assumption. Similarly, Unity computes the convolved mipmaps for reflection probes using its particular specular BDRF assumptions. One could potentially explore ImageBasedLighting.hlsl and try creating convolved mipmaps for other functions, but I except that it would be a ton of work, and few people would be able to tell the difference for most conventional specular models. 

One could try replacing the use of the spherical harmonic light probes with reflection probes with mipmaps crafted for diffuse lighting instead of specular lighting. If you set Texture Shape in the texture inspector to Cube, a Convolute Type dropdown menu appears with the options Specular (Glossy Reflections) and Diffuse (Irradiance). The Diffuse option results in much more blurring per mipmap level, as expected. Interestingly, that option doesn't seem to be used anywhere in stock Unity.

Monday, January 21, 2019

Running CodeWarrior for Playstation Net Yaroze via SheepShaver

The Net Yaroze was a special version of the original Sony Playstation that let consumers write their own games. To prevent Net Yaroze games from competing with official Playstation releases created by licensed companies with professional development kits, a Net Yaroze game couldn't access the CD drive, and it could usually only be played by people who had a Net Yaroze system. (Occasionally, Sony would put Net Yaroze games on demo CDs that came with magazines).

I bought a Net Yaroze system from eBay over decade ago, and in that time have been looking for students that would be interested in experimenting with it, with no success. Now, a decade later, I finally found some students on my Retrofuturistic Hardware Vertically Integrated Project (VIP) team interested in taking it on. Before handing it off, I thought I'd take a crack at getting the development environment running.

My Net Yaroze came with a CD labeled "Metrowerks Codewarrior for Net Yaroze," containing both Windows and Mac versions. I can't seem to find that particular CD at the moment, but I believe it corresponds to this disk image. It came out in 1996, which puts in in the zone of Windows 95 and Classic Mac OS System 7. As expected, the installer programs didn't seem to want run on my Windows 10 desktop machine from 2014 or my Mac OS X laptop from 2015.

SheepShaver emulates PowerPC-based Macintoshes. I presumed that Mac software that works on System 7 will likely work on System 9, so I installed System 9. This video by AlexElectronics was particularly helpful in getting SheepShaver up and running. I went with the "New World PPC ROM" and the "Mac OS 9 Boot Image" available from this page by Redundant Robot.

Click on the images to see the full-size screenshots. We're up and running:


Let's first install Adobe Acrobat Reader, which is conveniently available on the install CD:


Now let's run the installer!




OK, let's run CodeWarrior:


Let's try to compile this Hello World program:



We got some errors, but I'll save looking into those for another day.

At this point, I noticed there were folders on the CD labeled "Unsupported Software" and "Misc Goodies" that weren't copied over as part of the installation procedure, so I copied those over manually.


I didn't have the Net Yaroze on me at the time of doing these experiments, but I thought it would be fun to try running PSComUtil, the app that connects to the Net Yaroze.



Unsurprisingly, I got some errors.

Net Yaroze connects to its host computer via a DB9 RS232 serial port, so to even try to connect my 2015 MacBook Pro would require a USB-to-RS232 adapter. I'm also not sure too what extent SheepShaver supports serial communication through such an adapter.